Web components 是什么?
web components 就是网页组件式开发的技术规范。
web components 由三个独立的技术组成:
- Custom Elements(自定义元素):是用户使用一组 JavaScript API 自己定义生成的包含行为和标记名称的自定义模板,是完全有效的 HTML 元素。
- shadow DOM(影子 DOM):能够隔离 CSS 和 JavaScript。
- HTML templates(HTML 模板):用户在 HTML 中定义的模板,在调用之前不会呈现(<template>....</template>)。
web components 在主流浏览器中的到支持,对于像 IE 这种经常不支持新内容的浏览器我们有 polyfills 。
组成 web components 的三种技术可以独立使用,也可以与其他任何一种或两种技术结合使用,他们之间并不互相排斥。
HTML template
HTML 模板不会立即呈现,但可以通过 js 调用,并允许我们重复引用其内部结构。下面是模板的简单形式:
<template id="book-template">
<li><span class="title"></span> — <span class="author"></span></li>
</template>
<ul id="books"></ul>
示例模板在 js 调用之前不会呈现任何内容,需要我们实例化代码并告诉浏览器如何处理它。
// 选取模板的代码片段
const fragment = document.getElementById('book-template');
// 要展示的数据
const books = [
{ title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
{ title: 'A Farewell to Arms', author: 'Ernest Hemingway' },
{ title: 'Catch 22', author: 'Joseph Heller' }
];
books.forEach(book => {
// 复制模板中的内容,随后将其附加到相应的位置
const instance = document.importNode(fragment.content, true);
// 添加要展示的内容
instance.querySelector('.title').innerHTML = book.title;
instance.querySelector('.author').innerHTML = book.author;
// 将要展示的结构附加到 DOM
document.getElementById('books').appendChild(instance);
});
需要引起你注意的是 document.importNode
方法,此函数将创建模板内容的副本,并准备将其插入到另一个文档(或文档片段)中。函数的第一个参数是获取模板的内容,第二个参数是告诉浏览器对模板元素的 DOM 子树进行深层复制(即所有子节点)。
当然我们也可以不复制,直接使用 document.getElementById('books').appendChild(template.content
),但是这样做会把模版的内容从模版元素中删除并附加到 body 中。如果后续再要使用模板的内容将得到空文档片段。使用 document.importNode
允许我们在多个位置重用相同模板内容。
<template></template> 具有多功能性,允许我们把 css 和 js 都放在其内部,使其具有特定的样式和行为。
下面我们创建一个将要在后文使用到的对话框模板,demo1:
<template id="one-dialog">
<script>
// 打开弹框的操作
document.getElementById('launch-dialog').addEventListener('click', () => {
const wrapper = document.querySelector('.wrapper'); // 弹框
const closeButton = document.querySelector('button.close'); // 关闭按钮
const wasFocused = document.activeElement; // 当前激活的元素
wrapper.classList.add('open'); // 为弹框添加 open 属性
closeButton.focus(); // 关闭按钮聚集
closeButton.addEventListener('click', () => { // 关闭弹框的操作
wrapper.classList.remove('open'); // 移除弹框上 open 属性
wasFocused.focus(); // 激活元素聚焦 id=“launch-dialog” 的 button
});
});
</script>
<style>
.wrapper { opacity: 0; transition: visibility 0s, opacity 0.25s ease-in; }
.wrapper:not(.open) { visibility: hidden; }
.wrapper.open {
align-items: center; display: flex; justify-content: center;
height: 100vh;
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
opacity: 1; visibility: visible;
}
.overlay {
background: rgba(0, 0, 0, 0.8);
height: 100%; width: 100%;
position: fixed; top: 0; right: 0; bottom: 0; left: 0;
}
.dialog { background: #ffffff;
max-width: 600px;
padding: 1rem;
position: fixed;
}
button {
all: unset;
cursor: pointer;
font-size: 1.25rem;
position: absolute; top: 1rem; right: 1rem;
}
button:focus { border: 2px solid blue; }
</style>
<div class="wrapper">
<div class="overlay"></div>
<div class="dialog" role="dialog" aria-labelledby="title" aria-describedby="content">
<button class="close" aria-label="Close">✖️</button>
<h1 id="title">Hello world</h1>
<div id="content" class="content">
<p>This is content in the body of our modal</p>
</div>
</div>
</div>
</template>
<button id="launch-dialog">Launch dialog</button>
#launch-dialog {
background: tomato;
border-radius: 4px;
color: #fff;
font-family: Helvetica, Arial, sans-serif;
padding: 0.5rem 1rem;
position: static;
}
const template = document.getElementById('one-dialog');
document.body.appendChild(
document.importNode(template.content, true)
);
当我们使用模板时,你会发现模板中的样式和脚本没有只作用于我们的模板,而是应用于整个文档,当我们将多个模板实例添加到 DOM 时,会出现不太理想的效果。在下文中,我们将创建自己的元素,使用该模板并封装元素的行为。
Custom Elements
custom elements 是 web components 的核心。
custom elements 是 HTML 元素,就像 <div>
,<section>
或者 <article>
一样,我们可以通过浏览器 API 定义自己的名字,但是名字中必须包含一个或多个连接符(-),以便与原生 HTML 相区分,形如 <one-dialog></one-dialog>。同时,浏览器厂商不仅已经承诺不会在名称中创建包含连接符的新内置元素以防止冲突,还致力于保持规范持续向后兼容性。
自定义元素包含自己的语义,行为,标记,可以跨框架和浏览器共享。
从本质上讲,一个自定