diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
| commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
| tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/web_components | |
| parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
| download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip | |
initial commit
Diffstat (limited to 'files/zh-cn/web/web_components')
7 files changed, 1225 insertions, 0 deletions
diff --git a/files/zh-cn/web/web_components/html导入/index.html b/files/zh-cn/web/web_components/html导入/index.html new file mode 100644 index 0000000000..fe3aeb99cd --- /dev/null +++ b/files/zh-cn/web/web_components/html导入/index.html @@ -0,0 +1,61 @@ +--- +title: HTML 导入(HTML Imports) +slug: Web/Web_Components/HTML导入 +tags: + - HTML Imports + - Web Components +translation_of: Web/Web_Components/HTML_Imports +--- +<p>{{DefaultAPISidebar("Web Components")}}</p> + +<div class="blockIndicator obsolete"> +<p><strong>在 Google Chrome 73 后已过时</strong><br> + 此功能已过时。虽然它可能仍然适用于某些浏览器,但不鼓励使用它,因为它随时可能被删除。尽量避免使用它。</p> +</div> + +<div class="warning"> +<p>Firefox 将不会以当前形式发布HTML导入特性,<font><font>有关</font><font> 更多信息,</font><font>请参阅此</font></font><a href="https://hacks.mozilla.org/2015/06/the-state-of-web-components/" rel="noopener"><font><font>状态更新</font></font></a>。<font><font>在对标准达成共识或制定替代机制之前,您可以使用Google等的polyfill </font></font><code><a href="https://github.com/webcomponents/webcomponentsjs" rel="noopener">webcomponents.js</a></code><font><font>。</font></font> </p> +</div> + +<p><strong>HTML Imports </strong>旨在成为 <a href="/zh-CN/docs/Web/Web_Components">Web Components</a> 的打包机制,但也可以单独使用 HTML Imports。</p> + +<div>可以在HTML文档中使用<code><a href="/en-US/docs/Web/HTML/Element/link"><link></a></code> 标记导入HTML文件,如下所示:</div> + +<div> </div> + +<pre><link rel="import" href="myfile.html"></pre> + +<p><span style="line-height: 1.5;">链接类型 </span><code style="font-style: normal; line-height: 1.5;">import</code><span style="line-height: 1.5;"> 是新加入的。</span></p> + +<h2 id="规范">规范</h2> + +<table class="spec-table standard-table"> + <tbody> + <tr> + <th scope="col">规范</th> + <th scope="col">状态</th> + <th scope="col">评论</th> + </tr> + <tr> + <td>{{SpecName('HTML Imports', "", "")}}</td> + <td>{{Spec2('HTML Imports')}}</td> + <td>初步定义</td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容性"><font><font>浏览器兼容性</font></font></h2> + +<p class="hidden">The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</p> + +<p>{{Compat("html.elements.link.rel.import")}}</p> + +<h2 id="阅读更多"><strong>阅读更多:</strong></h2> + +<ul> + <li>https://developer.mozilla.org/zh-CN/docs/Web/Web_Components</li> + <li>https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Custom_Elements</li> + <li>https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/template</li> + <li>https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Shadow_DOM<br> + </li> +</ul> diff --git a/files/zh-cn/web/web_components/index.html b/files/zh-cn/web/web_components/index.html new file mode 100644 index 0000000000..bb6c25179f --- /dev/null +++ b/files/zh-cn/web/web_components/index.html @@ -0,0 +1,215 @@ +--- +title: Web Components +slug: Web/Web_Components +tags: + - Landing + - Web Components +translation_of: Web/Web_Components +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<div class="summary"> +<p>Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。</p> +</div> + +<h2 id="概念和使用">概念和使用</h2> + +<p>作为开发者,我们都知道尽可能多的重用代码是一个好主意。这对于自定义标记结构来说通常不是那么容易 — 想想复杂的HTML(以及相关的样式和脚本),有时您不得不写代码来呈现自定义UI控件,并且如果您不小心的话,多次使用它们会使您的页面变得一团糟。</p> + +<p>Web Components旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。</p> + +<ul> + <li><strong>Custom elements(自定义元素):</strong>一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。</li> + <li><strong>Shadow DOM(影子DOM)</strong>:一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。</li> + <li><strong>HTML templates(HTML模板):</strong> {{HTMLElement("template")}} 和 {{HTMLElement("slot")}} 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。</li> +</ul> + +<p>实现web component的基本方法通常如下所示:</p> + +<ol> + <li>创建一个类或函数来指定web组件的功能,如果使用类,请使用 ECMAScript 2015 的类语法(参阅<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes">类</a>获取更多信息)。</li> + <li>使用 {{domxref("CustomElementRegistry.define()")}} 方法注册您的新自定义元素 ,并向其传递要定义的元素名称、指定元素功能的类、以及可选的其所继承自的元素。</li> + <li>如果需要的话,使用{{domxref("Element.attachShadow()")}} 方法将一个shadow DOM附加到自定义元素上。使用通常的DOM方法向shadow DOM中添加子元素、事件监听器等等。</li> + <li>如果需要的话,使用 {{htmlelement("template")}} 和{{htmlelement("slot")}} 定义一个HTML模板。再次使用常规DOM方法克隆模板并将其附加到您的shadow DOM中。</li> + <li>在页面任何您喜欢的位置使用自定义元素,就像使用常规HTML元素那样。</li> +</ol> + +<h2 id="教程">教程</h2> + +<dl> + <dt><a href="/zh-CN/docs/Web/Web_Components/Using_custom_elements">Using custom elements</a></dt> + <dd>介绍如何使用自定义元素的功能来创建简单的 web components,以及生命周期回调和其他更高级的功能。</dd> + <dt><a href="/zh-CN/docs/Web/Web_Components/Using_shadow_DOM">Using shadow DOM</a></dt> + <dd>介绍 shadow DOM 的基础知识,展示如何向元素中附加shadow DOM,添加到shadow DOM树,添加样式等等。</dd> + <dt><a href="/zh-CN/docs/Web/Web_Components/Using_templates_and_slots">Using templates and slots</a></dt> + <dd>介绍如何使用{{htmlelement("template")}} 和 {{htmlelement("slot")}} 元素定义可重用的HTML结构,然后在Web components中使用该结构。</dd> +</dl> + +<h2 id="参考">参考</h2> + +<h3 id="Custom_elements">Custom elements</h3> + +<dl> + <dt>{{domxref("CustomElementRegistry")}}</dt> + <dd>包含自定义元素相关功能,最值得注意的是 {{domxref("CustomElementRegistry.define()")}} 方法用来注册新的自定义元素,这样就可以在文档中使用它们。</dd> + <dt>{{domxref("Window.customElements")}}</dt> + <dd>返回 <code>CustomElementRegistry</code> 对象的引用。</dd> + <dt><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks">生命周期回调</a></dt> + <dd>定义在自定义元素的类定义中的特殊回调函数,影响其行为: + <ul> + <li><code>connectedCallback</code>: 当自定义元素第一次被连接到文档DOM时被调用。</li> + <li><code>disconnectedCallback</code>: 当自定义元素与文档DOM断开连接时被调用。</li> + <li><code>adoptedCallback</code>: 当自定义元素被移动到新文档时被调用。</li> + <li><code>attributeChangedCallback</code>: 当自定义元素的一个属性被增加、移除或更改时被调用。</li> + </ul> + </dd> + <dd> + <ul> + </ul> + </dd> +</dl> + +<dl> + <dt>创建自定义内置元素的扩展</dt> + <dd> + <ul> + <li>{{htmlattrxref("is")}} 全局 HTML 属性:允许您指定一个标准HTML元素应该表现得像一个已注册的自定义内置元素。</li> + <li>{{domxref("Document.createElement()")}} 方法的“is”选项:允许您创建一个标准HTML元素的实例,表现得像一个给定的已注册的自定义内置元素。</li> + </ul> + </dd> + <dt>CSS 伪类</dt> + <dd>与自定义元素特别相关的伪类: + <ul> + <li>{{cssxref(":defined")}}: 匹配任何已定义的元素,包括内置元素和使用<code>CustomElementRegistry.define()</code>定义的自定义元素。</li> + <li>{{cssxref(":host")}}: 选择 <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM </a>的 shadow host ,内容是它内部使用的 CSS( containing the CSS it is used inside )。</li> + <li>{{cssxref(":host()")}}: 选择 <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM </a>的 shadow host ,内容是它内部使用的 CSS (这样您可以从 shadow DOM 内部选择自定义元素)— 但只匹配给定方法的选择器的 shadow host 元素。</li> + <li>{{cssxref(":host-context()")}}: 选择 <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM </a>的 shadow host ,内容是它内部使用的 CSS (这样您可以从 shadow DOM 内部选择自定义元素)— 但只匹配给定方法的选择器匹配元素的子 shadow host 元素。</li> + </ul> + </dd> +</dl> + +<h3 id="Shadow_DOM">Shadow DOM</h3> + +<dl> + <dt>{{domxref("ShadowRoot")}}</dt> + <dd>表示shadow DOM子树的根节点。</dd> + <dt>{{domxref("DocumentOrShadowRoot")}}</dt> + <dd>定义了可在文档和shadow根中使用的功能的mixin。</dd> + <dt>{{domxref("Element")}} extensions</dt> + <dd>与shadow DOM有关的<code>Element</code> 接口的扩展: + <ul> + <li>{{domxref("Element.attachShadow()")}} 方法将shadow DOM树附加给特定元素。</li> + <li>{{domxref("Element.shadowRoot")}} 属性返回附加给特定元素的shadow root,或者 <code>null</code> 如果没有shadow root被附加。</li> + </ul> + </dd> + <dt>{{domxref("Node")}} 相关拓展</dt> + <dd>与 shadow DOM 相关的 <code>Node</code> 接口的拓展: + <ul> + <li>{{domxref("Node.getRootNode()")}} 方法返回上下文对象的根,可以选择包含shadow root,如果可用的话。</li> + <li>{{domxref("Node.isConnected")}} 属性返回一个布尔值表示节点是否连接(直接或间接)到上下文对象。例如,在普通DOM的情况下为{{domxref("Document")}} 对象,或者在shadow DOM的情况下为 {{domxref("ShadowRoot")}} 。</li> + </ul> + </dd> + <dt>{{domxref("Event")}} 拓展</dt> + <dd>与shadow DOM相关的<code>Event</code> 接口的扩展:</dd> + <dd> + <ul> + <li>{{domxref("Event.composed")}}: 返回 {{jsxref("Boolean")}} 它表明事件是否会通过shadow DOM边界传播到标准DOM。</li> + <li>返回事件的路径(侦听器将被调用的对象)。如果shadow root是使用{{domxref("ShadowRoot.mode")}}为closed创建的,则不包括shadow树中的节点。</li> + </ul> + </dd> +</dl> + +<h3 id="HTML_templates">HTML templates</h3> + +<dl> + <dt>{{htmlelement("template")}}</dt> + <dd>包含一个HTML片段,不会在文档初始化时渲染。但是可以在运行时使用JavaScript显示。主要用作自定义元素结构的基础。关联的DOM接口是{{domxref("HTMLTemplateElement")}}。</dd> + <dt>{{htmlelement("slot")}}</dt> + <dd>web component中的一个占位符,你可以填充自己的标记,这样你就可以创建单独的DOM树并将它们呈现在一起。关联的DOM接口是{{domxref("HTMLSlotElement")}}。</dd> + <dt>The <code><a href="/en-US/docs/Web/HTML/Global_attributes/slot">slot</a></code> global HTML attribute</dt> + <dd>将在shadow DOM树中的插槽分配给一个元素。</dd> + <dt>{{domxref("Slotable")}}</dt> + <dd>由 {{domxref("Element")}} 和 {{domxref("Text")}} 节点实现的mixin,定义了允许它们成为 {{htmlelement("slot")}} 元素内容的特性。mixin定义了一个属性, {{domxref("Slotable.assignedSlot")}},返回节点所插入的插槽的引用。</dd> +</dl> + +<dl> + <dt>{{domxref("Element")}} extensions</dt> + <dd>与插槽相关的<code>Element</code>接口的扩展: + <ul> + <li>{{domxref("Element.slot")}}: 返回附加到元素上的shadow DOM插槽的名字。</li> + </ul> + </dd> + <dt>CSS pseudo-elements</dt> + <dd>slots特别相关的伪元素: + <ul> + <li>{{cssxref("::slotted")}}: 匹配任何已经插入一个slot的内容。</li> + </ul> + </dd> + <dt>The {{event("slotchange")}} event</dt> + <dd>当插槽中的节点改变时在 {{domxref("HTMLSlotElement")}} 实例(<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/slot" title="HTML <slot> element—是 Web Components 技术的一部分,是自定义web组件的占位符,目的是分离创建DOM树和DOM树的表现()。"><code><slot></code></a> 元素)上触发。</dd> +</dl> + +<h2 id="例子">例子</h2> + +<p>在 <a href="https://github.com/mdn/web-components-examples">web-components-examples</a>,我们正在构建一些例子。以后会添加更多。</p> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName("HTML WHATWG","scripting.html#the-template-element","<template> element")}}</td> + <td>{{Spec2("HTML WHATWG")}}</td> + <td>The definition of {{HTMLElement("template")}}.</td> + </tr> + <tr> + <td>{{SpecName("HTML WHATWG","custom-elements.html#custom-elements","custom elements")}}</td> + <td>{{Spec2("HTML WHATWG")}}</td> + <td>The definition of <a href="/en-US/docs/Web/Web_Components/Using_custom_elements">HTML Custom Elements</a>.</td> + </tr> + <tr> + <td>{{SpecName("DOM WHATWG","#shadow-trees","shadow trees")}}</td> + <td>{{Spec2("DOM WHATWG")}}</td> + <td>The definition of <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a>.</td> + </tr> + <tr> + <td>{{SpecName("HTML Imports", "", "")}}</td> + <td>{{Spec2("HTML Imports")}}</td> + <td>Initial <a href="/en-US/docs/Web/Web_Components/HTML_Imports">HTML Imports</a> definition.</td> + </tr> + <tr> + <td>{{SpecName("Shadow DOM", "", "")}}</td> + <td>{{Spec2("Shadow DOM")}}</td> + <td>Initial <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a> definition.</td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容性">浏览器兼容性</h2> + +<p>通常来说:</p> + +<ul> + <li>Firefox(版本63)、Chrome和Opera都默认支持Web组件。</li> + <li>Safari支持许多web组件特性,但比上述浏览器少。</li> + <li>Edge正在开发一个实现。</li> +</ul> + +<p>要获得特定特性的详细浏览器支持,您必须参考上面列出的参考页面。</p> + +<h2 id="另见">另见</h2> + +<ul> + <li><a href="https://www.webcomponents.org/">webcomponents.org</a> — site featuring web components examples, tutorials, and other information.</li> + <li><a href="https://github.com/hybridsjs/hybrids">Hybrids</a> — Open source web components library, which favors plain objects and pure functions over <code>class</code> and <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> syntax. It provides a simple and functional API for creating custom elements.</li> + <li><a href="https://www.polymer-project.org/">Polymer</a> — Google's web components framework — a set of polyfills, enhancements, and examples. Currently the easiest way to use web components cross-browser.</li> + <li><a href="https://github.com/devpunks/snuggsi#readme">Snuggsi.es</a> — Easy Web Components in ~1kB <em>Including polyfill</em> — All you need is a browser and basic understanding of HTML, CSS, and JavaScript classes to be productive.</li> + <li><a href="https://github.com/slimjs/slim.js">Slim.js</a> — Open source web components library — a high-performant library for rapid and easy component authoring; extensible and pluggable and cross-framework compatible.</li> + <li><a href="https://www.htmlelements.com/">Smart.js</a> — Web Components library with simple API for creating cross-browser custom elements.</li> + <li><a href="https://stenciljs.com/">Stencil</a> — Toolchain for building reusable, scalable design systems in web components.</li> +</ul> diff --git a/files/zh-cn/web/web_components/status_in_firefox/index.html b/files/zh-cn/web/web_components/status_in_firefox/index.html new file mode 100644 index 0000000000..d57e5adef5 --- /dev/null +++ b/files/zh-cn/web/web_components/status_in_firefox/index.html @@ -0,0 +1,51 @@ +--- +title: Status of Web Components support in Firefox +slug: Web/Web_Components/Status_in_Firefox +translation_of: Web/Web_Components/Status_in_Firefox +--- +<p>{{DefaultAPISidebar("Web Components")}}{{SeeCompatTable}}</p> + +<p><a href="/en-US/docs/Web/Web_Components">Web Components</a> 依旧是一项非常新的技术,它的规范正随着浏览器的实现而不断演变并且 Web 开发者正在测试和使用它。 它的实现状态是变化的并且演变的十分迅速; 这篇文章列出了在 <a href="/en-US/docs/Mozilla/Gecko">Gecko</a> 上的状态, 用于 Firefox 和Firefox OS.</p> + +<div class="blob instapaper_body" id="readme"> +<article class="markdown-body entry-content"> +<h2 id="原生支持">原生支持</h2> + +<p>下面的特征已经被实现了并且默认在 Firefox and Firefox OS 中被激活:</p> + +<ul> + <li>{{HTMLElement("template")}}</li> +</ul> + +<h2 id="即将到来的特征">即将到来的特征</h2> + +<ul> + <li>一个实现关于新的 <a href="/en-US/docs/Web/Web_Components/Shadow_DOM">Shadow DOM</a> 共识有望在2016年第一季度达成; <a href="https://annevankesteren.nl/2015/07/shadow-dom-custom-elements-update">Anne's</a> 和 <a href="https://hacks.mozilla.org/2015/06/the-state-of-web-components/">Wilson's</a> 的博客讲述了这些细节。 这依然有 <a href="https://github.com/w3c/webcomponents/labels/shadow-dom">大量的讨论和公开问题</a> 关于这个规范.。并且所有的浏览器实现被有望在未来得到更新.</li> + <li><strong>自定义元素</strong> 是从头开始, 用一种方式来重建它们使用 ECMAScript 6 “class” 语法 (换而言之, 更少的使用基于原型的语法). 苹果公司的 Ryosuke Niwa 正在填补某些实验性功能使用新的途径. + <ul> + <li>旧的语法将可以与新的语法一起在Chrome 中工作一段时间(例如, {{domxref("Element.createShadowRoot()")}} 对应 {{domxref("Element.attachShadow()")}}), 但不能原生的在Firefox中工作。</li> + </ul> + </li> + <li>这将会有一个供应商 <a href="https://github.com/w3c/WebPlatformWG/blob/gh-pages/meetings/29janWC.md">面对面交流的机会在2016年一月</a> 来讨论问来会出现的问题 。</li> +</ul> + +<h2 id="被摒弃的功能">被摒弃的功能</h2> + +<p>这些功能已被考虑实现了, 并且有些是实验性实现。但他们将会永远不被实现, 或者被删除。</p> + +<ul> + <li><strong>HTML imports</strong>, 因为我们想等着看看开发者如何使用ES6 模块 (虽然还没有实现; 查看 {{bug(568953)}}). imports是一个早期未完成实现,并且将会被删除从Firefox中。</li> +</ul> + +<h2 id="在Firefox中使用垫片">在Firefox中使用垫片</h2> + +<p>这有些注意事项在Firefox中使用垫片的时候:</p> + +<ul> + <li>当你激活原生Web容器支持在Firefox中通过设置 {{pref("dom.webcomponents.enabled")}} 偏好 为 <code>true</code> 在 <code>about:config 中</code>, 这个未完成的原生实现开始运作并且垫片可能会出现混淆; 这会有很大的可能性出现崩溃.</li> + <li>一个使用 <a href="https://github.com/webcomponents/webcomponentsjs">webcomponents.js</a> 垫片生成的Shadow DOM 并没有完全包裹样式, 所以这个 样式 可能会溢出。 要注意使用垫片构建的网址当运行在不支持原生Shadow DOM的环境之下时可能会出现差异.</li> + <li>这个Shadow DOM 垫片运行时非常缓慢的以为他重写了DOM元素的原型来挂在它的功能 。</li> + <li>如果你不需要使用 Shadow DOM, 使用 <a href="https://github.com/webcomponents/webcomponentsjs">webcomponents-lite.js</a> 版本的 webcomponents.js 垫片是一个名明智的选择; 这个版本不填补 Shadow DOM.</li> +</ul> +</article> +</div> diff --git a/files/zh-cn/web/web_components/using_custom_elements/index.html b/files/zh-cn/web/web_components/using_custom_elements/index.html new file mode 100644 index 0000000000..2beef20c92 --- /dev/null +++ b/files/zh-cn/web/web_components/using_custom_elements/index.html @@ -0,0 +1,253 @@ +--- +title: 使用 custom elements +slug: Web/Web_Components/Using_custom_elements +translation_of: Web/Web_Components/Using_custom_elements +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<p class="summary">Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签),而往常,开发者不得不写一大堆冗长、深层嵌套的标签来实现同样的页面功能。这篇文章将会介绍如何使用HTML的custom elements。</p> + +<div class="note"> +<p><strong>注意:</strong>Firefox、Chrome和Opera默认就支持 custom elements。Safari目前只支持 autonomous custom elements(自主自定义标签),而 Edge也正在积极实现中。</p> +</div> + +<h2 id="概述">概述</h2> + +<p>{{domxref("CustomElementRegistry")}} 接口的实例用来处理 web 文档中的 custom elements — 该对象允许你注册一个 custom element,返回已注册 custom elements 的信息,等等。</p> + +<p>{{domxref("CustomElementRegistry.define()")}} 方法用来注册一个 custom element,该方法接受以下参数:</p> + +<ul> + <li>表示所创建的元素名称的符合 {{domxref("DOMString")}} 标准的字符串。注意,custom element 的名称不能是单个单词,且其中<a href="https://html.spec.whatwg.org/#valid-custom-element-name">必须要有短横线</a>。</li> + <li>用于定义元素行为的 <a href="/en-US/docs/Web/JavaScript/Reference/Classes">类</a> 。</li> + <li><code>可选参数</code>,一个包含 <code>extends</code> 属性的配置对象,是可选参数。它指定了所创建的元素继承自哪个内置元素,可以继承任何内置元素。</li> +</ul> + +<p>作为示例,我们可以像这样定义一个叫做 <a href="https://mdn.github.io/web-components-examples/word-count-web-component/">word-count</a> 的 custom element:</p> + +<pre class="brush: js notranslate">customElements.define('word-count', WordCount, { extends: 'p' });</pre> + +<p>这个元素叫做 <code>word-count</code>,它的类对象是 <code>WordCount</code>, 继承自 {{htmlelement("p")}} 元素.</p> + +<p>一个 custom element 的类对象可以通过 ES 2015 标准里的类语法生成。所以,<code>WordCount</code>可以写成下面这样:</p> + +<pre class="brush: js notranslate">class WordCount extends HTMLParagraphElement { + constructor() { + // 必须首先调用 super 方法 + super(); + + // 元素的功能代码写在这里 + + ... + } +}</pre> + +<p>上面只是一个简单的例子,我们能做的不只这些。在构造函数中,我们可以设定一些生命周期的回调函数,在特定的时间,这些回调函数将会被调用。例如,<code>connectedCallback</code>会在 custom element首次被插入到文档DOM节点上时被调用,而 <code>attributeChangedCallback</code>则会在 custom element增加、删除或者修改某个属性时被调用。</p> + +<p>你可以在 {{anch("使用生命周期回调函数")}}段落中了解更多相关信息。</p> + +<p>共有两种 custom elements:</p> + +<ul> + <li><strong>Autonomous custom elements </strong>是独立的元素,它不继承其他内建的HTML元素。你可以直接把它们写成HTML标签的形式,来在页面上使用。例如 <code><popup-info></code>,或者是<code>document.createElement("popup-info")</code>这样。</li> + <li><strong>Customized built-in elements</strong> 继承自基本的HTML元素。在创建时,你必须指定所需扩展的元素(正如上面例子所示),使用时,需要先写出基本的元素标签,并通过 {{htmlattrxref("is")}} 属性指定custom element的名称。例如<code><p is="word-count"></code>, 或者 <code>document.createElement("p", { is: "word-count" })</code>。</li> +</ul> + +<h2 id="示例">示例</h2> + +<p>让我们来看几个简单示例,来了解如何创建 custom elements。</p> + +<h3 id="Autonomous_custom_elements">Autonomous custom elements</h3> + +<p>我们来看一下 <code><a href="https://github.com/mdn/web-components-examples/tree/master/popup-info-box-web-component"><popup-info-box></a></code> (查看<a href="https://mdn.github.io/web-components-examples/popup-info-box-web-component/">在线示例</a>),一个关于 autonomous custom element的例子。它包含有一个图标和一段文字,并且图标显示在页面上。在这个图标获取焦点时,它会显示一个包含该段文字的信息框,用于展示更多的信息。</p> + +<p>为了实现这个功能,首先创建一个JavaScript文件,定义一个叫做<code>PopUpInfo</code>的类,它继承自{{domxref("HTMLElement")}}。Autonomous custom elements 总是继承自<code>HTMLElement</code>。</p> + +<pre class="brush: js notranslate">class PopUpInfo extends HTMLElement { + constructor() { + // 必须首先调用 super方法 + super(); + + // 元素的功能代码写在这里 + + ... + } +}</pre> + +<p>上述代码片段中,类的构造函数{{jsxref("Classes.constructor","constructor")}}总是先调用<code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/super">super()</a></code>来建立正确的原型链继承关系。</p> + +<p>在构造函数中,我们会定义元素实例所拥有的全部功能。作为示例,我们首先会将shadow root附加到custom element上,然后通过一系列DOM操作创建custom element的内部阴影DOM结构,再将其附加到 shadow root上,最后再将一些CSS附加到 shadow root的style节点上。</p> + +<pre class="brush: js notranslate">// 创建一个 shadow root +var shadow = this.attachShadow({mode: 'open'}); + +// 创建一个 spans +var wrapper = document.createElement('span'); +wrapper.setAttribute('class','wrapper'); +var icon = document.createElement('span'); +icon.setAttribute('class','icon'); +icon.setAttribute('tabindex', 0); +var info = document.createElement('span'); +info.setAttribute('class','info'); + +// 获取text属性上的内容,并添加到一个span标签内 +var text = this.getAttribute('text'); +info.textContent = text; + +// 插入 icon +var imgUrl; +if(this.hasAttribute('img')) { + imgUrl = this.getAttribute('img'); +} else { + imgUrl = 'img/default.png'; +} +var img = document.createElement('img'); +img.src = imgUrl; +icon.appendChild(img); + +// 创建一些 CSS,并应用到 shadow dom上 +var style = document.createElement('style'); + +style.textContent = '.wrapper {' + +// 简洁起见,省略了具体的CSS + +// 将创建的元素附加到 shadow dom + +shadow.appendChild(style); +shadow.appendChild(wrapper); +wrapper.appendChild(icon); +wrapper.appendChild(info);</pre> + +<p>最后,我们使用之前提到的<code>define()</code>方法将 custom element注册到<code>CustomElementRegistry</code>上,在方法的参数里,我们指定了元素的名称,以及定义了元素功能的类。</p> + +<pre class="brush: js notranslate">customElements.define('popup-info', PopUpInfo);</pre> + +<p>现在我们可以在页面上使用我们定义的custom element了,就像下面这样:</p> + +<pre class="brush: html notranslate"><popup-info img="img/alt.png" text="Your card validation code (CVC) + is an extra security feature — it is the last 3 or 4 numbers on the + back of your card."></pre> + +<div class="note"> +<p><strong>注意</strong>: 上方代码不是最新,你可以在这里找到<a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-web-component/main.js">完整的源码</a>。</p> +</div> + +<div class="note"> +<p><strong>译者提示</strong>: 在Chrome版本76.0.3809.132(正式版本)(64 位)中测试发现,<code>customElements.define()</code>必须在js文件中调用,且引用此js文件时必须在<code>script</code>标签上添加<code>defer</code>属性,否则<code>this.getAttribute('属性名称')</code>无法获取到值。</p> +</div> + +<h3 id="Customized_built-in_elements">Customized built-in elements</h3> + +<p>现在让我们来看一下另一个有关customized built in element(自定义内置元素)的示例— <a href="https://github.com/mdn/web-components-examples/tree/master/expanding-list-web-component">expanding-list</a> (<a href="https://mdn.github.io/web-components-examples/expanding-list-web-component/">查看在线示例</a>)。该示例将所有的无序列表转化为一个可收起/展开的菜单。</p> + +<p>首先,我们定义一个元素的类,这和之前一样:</p> + +<pre class="brush: js notranslate">class ExpandingList extends HTMLUListElement { + constructor() { + // 必须首先调用 super方法 + super(); + + // 元素的功能代码写在这里 + + ... + } +}</pre> + +<p>在这里,我们不会详细解释元素的功能细节,你可以在源码中了解它的工作方式。这里的真正不同点在于元素继承的是{{domxref("HTMLUListElement")}} 接口,而不是{{domxref("HTMLElement")}}。所以它拥有{{htmlelement("ul")}} 元素所有的特性,以及在此基础上我们定义的功能,这是与独立元素(standalone element)不同之处。这也是为什么我们称它为 customized built-in元素,而不是一个autonomous元素。</p> + +<p>接下来,和之前一样,我们使用<code>define()</code>方法注册一个元素,但不同的是,我们需要添加一个配置对象,用于指定我们需要继承的元素:</p> + +<pre class="brush: js notranslate">customElements.define('expanding-list', ExpandingList, { extends: "ul" });</pre> + +<p>在页面上使用 built-in element看起来也会有所不同:</p> + +<pre class="brush: html notranslate"><ul is="expanding-list"> + + ... + +</ul></pre> + +<p>你可以正常使用<code><ul></code>标签,也可以通过<code>is</code>属性来指定一个custom element的名称。</p> + +<div class="note"> +<p><strong>注意</strong>: 同样的,你可以在这里找到完整的 <a href="https://github.com/mdn/web-components-examples/blob/master/expanding-list-web-component/main.js">JavaScript 源码</a>。</p> +</div> + +<div class="note"> +<p><strong>译者注:在 </strong>chrome 66 版本上,该示例无法正确工作,相关问题:</p> + +<p><a href="https://stackoverflow.com/questions/39986046/how-to-create-new-instance-of-an-extended-class-of-custom-elements">How to create new instance of an extended class of custom elements</a></p> +</div> + +<h2 id="使用生命周期回调函数">使用生命周期回调函数</h2> + +<p>在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:</p> + +<ul> + <li><code>connectedCallback</code>:当 custom element首次被插入文档DOM时,被调用。</li> + <li><code>disconnectedCallback</code>:当 custom element从文档DOM中删除时,被调用。</li> + <li><code>adoptedCallback</code>:当 custom element被移动到新的文档时,被调用。</li> + <li><code>attributeChangedCallback</code>: 当 custom element增加、删除、修改自身属性时,被调用。</li> +</ul> + +<p>我们来看一下它们的一下用法示例。下面的代码出自<a href="https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks">life-cycle-callbacks</a>示例(<a href="https://mdn.github.io/web-components-examples/life-cycle-callbacks/">查看在线示例</a>)。这个简单示例只是生成特定大小、颜色的方块。custom element看起来像下面这样:</p> + +<pre class="brush: html notranslate"><custom-square l="100" c="red"></custom-square></pre> + +<p>这里,类的构造函数很简单 — 我们将 shadow DOM附加到元素上,然后将一个{{htmlelement("div")}}元素和{{htmlelement("style")}}元素附加到 shadow root上:</p> + +<pre class="brush: js notranslate">var shadow = this.attachShadow({mode: 'open'}); + +var div = document.createElement('div'); +var style = document.createElement('style'); +shadow.appendChild(style); +shadow.appendChild(div);</pre> + +<p>示例中的关键函数是 <code>updateStyle()</code>—它接受一个元素作为参数,然后获取该元素的shadow root,找到<code><style></code>元素,并添加{{cssxref("width")}},{{cssxref("height")}}以及{{cssxref("background-color")}}样式。</p> + +<pre class="brush: js notranslate">function updateStyle(elem) { + var shadow = elem.shadowRoot; + var childNodes = shadow.childNodes; + for(var i = 0; i < childNodes.length; i++) { + if(childNodes[i].nodeName === 'STYLE') { + childNodes[i].textContent = 'div {' + + ' width: ' + elem.getAttribute('l') + 'px;' + + ' height: ' + elem.getAttribute('l') + 'px;' + + ' background-color: ' + elem.getAttribute('c'); + } + } +}</pre> + +<p>实际的更新操作是在生命周期的回调函数中处理的,我们在构造函数中设定类这些回调函数。当元素插入到DOM中时,<code>connectedCallback()</code>函数将会执行 — 在该函数中,我们执行<code>updateStyle()</code> 函数来确保方块按照定义来显示;</p> + +<pre class="brush: js notranslate">connectedCallback() { + console.log('Custom square element added to page.'); + updateStyle(this); +}</pre> + +<p><code>disconnectedCallback()</code>和<code>adoptedCallback()</code>回调函数只是简单地将消息发送到控制台,提示我们元素什么时候从DOM中移除、或者什么时候移动到不同的页面:</p> + +<pre class="brush: js notranslate">disconnectedCallback() { + console.log('Custom square element removed from page.'); +} + +adoptedCallback() { + console.log('Custom square element moved to new page.'); +}</pre> + +<p>每当元素的属性变化时,<code>attributeChangedCallback()</code>回调函数会执行。正如它的属性所示,我们可以查看属性的名称、旧值与新值,以此来对元素属性做单独的操作。在当前的示例中,我们只是再次执行了<code>updateStyle()</code>函数,以确保方块的样式在元素属性值变化后得以更新:</p> + +<pre class="brush: js notranslate">attributeChangedCallback(name, oldValue, newValue) { + console.log('Custom square element attributes changed.'); + updateStyle(this); +}</pre> + +<p><code><font face="Open Sans, arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">需要注意的是,如果需要在元素属性变化后,触发 </span></font>attributeChangedCallback()</code>回调函数,你必须监听这个属性。这可以通过定义<code>observedAttributes()</code> get函数来实现,<code>observedAttributes()</code>函数体内包含一个 return语句,返回一个数组,包含了需要监听的属性名称:</p> + +<pre class="brush: js notranslate">static get observedAttributes() {return ['w', 'l']; }</pre> + +<p>在我们的例子中,该段代码处于构造函数的上方。</p> + +<div class="note"> +<p><strong>注意</strong>: 在这里查看<a href="https://github.com/mdn/web-components-examples/blob/master/life-cycle-callbacks/main.js">完整的 JavaScript源码</a>。</p> +</div> diff --git a/files/zh-cn/web/web_components/using_shadow_dom/index.html b/files/zh-cn/web/web_components/using_shadow_dom/index.html new file mode 100644 index 0000000000..1267f49508 --- /dev/null +++ b/files/zh-cn/web/web_components/using_shadow_dom/index.html @@ -0,0 +1,221 @@ +--- +title: 使用 shadow DOM +slug: Web/Web_Components/Using_shadow_DOM +tags: + - API + - DOM + - Web Components + - 指南 +translation_of: Web/Web_Components/Using_shadow_DOM +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<p class="summary">Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。本篇文章将会介绍 Shadow DOM 的基础使用。</p> + +<div class="note"> +<p><strong>备注:</strong> Firefox(从版本 63 开始),Chrome,Opera 和 Safari 默认支持 Shadow DOM。基于 Chromium 的新 Edge 也支持 Shadow DOM;而旧 Edge 未能撑到支持此特性。</p> +</div> + +<h2 id="概况">概况</h2> + +<p>本文章假设你对 <a href="/en-US/docs/Web/API/Document_Object_Model/Introduction">DOM(文档对象模型)</a>有一定的了解,它是由不同的元素节点、文本节点连接而成的一个树状结构,应用于标记文档中(例如 Web 文档中常见的 HTML 文档)。请看如下示例,一段 HTML 代码:</p> + +<pre class="brush: html"><!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Simple DOM example</title> + </head> + <body> + <section> + <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth."> + <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p> + </section> + </body> +</html></pre> + +<p>这个片段会生成如下的 DOM 结构:</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/14559/dom-screenshot.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p> + +<p><em>Shadow</em> DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中——它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15788/shadow-dom.png" style="height: 543px; width: 1138px;"></p> + +<p>这里,有一些 Shadow DOM 特有的术语需要我们了解:</p> + +<ul> + <li>Shadow host:一个常规 DOM节点,Shadow DOM 会被附加到这个节点上。</li> + <li>Shadow tree:Shadow DOM内部的DOM树。</li> + <li>Shadow boundary:Shadow DOM结束的地方,也是常规 DOM开始的地方。</li> + <li>Shadow root: Shadow tree的根节点。</li> +</ul> + +<p>你可以使用同样的方式来操作 Shadow DOM,就和操作常规 DOM 一样——例如添加子节点、设置属性,以及为节点添加自己的样式(例如通过 <code>element.style</code> 属性),或者为整个 Shadow DOM 添加样式(例如在 {{htmlelement("style")}} 元素内添加样式)。不同的是,Shadow DOM 内部的元素始终不会影响到它外部的元素(除了 {{CSSxRef(":focus-within")}}),这为封装提供了便利。</p> + +<p>注意,不管从哪个方面来看,Shadow DOM 都不是一个新事物——在过去的很长一段时间里,浏览器用它来封装一些元素的内部结构。以一个有着默认播放控制按钮的 {{htmlelement("video")}} 元素为例。你所能看到的只是一个 <code><video></code> 标签,实际上,在它的 Shadow DOM 中,包含来一系列的按钮和其他控制器。Shadow DOM 标准允许你为你自己的元素(custom element)维护一组 Shadow DOM。</p> + +<h2 id="基本用法">基本用法</h2> + +<p>可以使用 {{domxref("Element.attachShadow()")}} 方法来将一个 shadow root 附加到任何一个元素上。它接受一个配置对象作为参数,该对象有一个 <code>mode</code> 属性,值可以是 <code>open</code> 或者 <code>closed</code>:</p> + +<pre class="brush: js">let shadow = elementRef.attachShadow({mode: 'open'}); +let shadow = elementRef.attachShadow({mode: 'closed'});</pre> + +<p><code>open</code> 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM,例如使用 {{domxref("Element.shadowRoot")}} 属性:</p> + +<pre class="brush: js">let myShadowDom = myCustomElem.shadowRoot;</pre> + +<p>如果你将一个 Shadow root 附加到一个 Custom element 上,并且将 <code>mode</code> 设置为 <code>closed</code>,那么就不可以从外部获取 Shadow DOM 了——<code>myCustomElem.shadowRoot</code> 将会返回 <code>null</code>。浏览器中的某些内置元素就是如此,例如<code><video></code>,包含了不可访问的 Shadow DOM。</p> + +<div class="note"> +<p><strong>备注:</strong> 正如<a href="https://blog.revillweb.com/open-vs-closed-shadow-dom-9f3d7427d1af">这篇文章所阐述的</a>,处理 closed Shadow DOM 实际上很简单,往往也很值得这么做。</p> +</div> + +<p>如果你想将一个 Shadow DOM 附加到 custom element 上,可以在 custom element 的构造函数中添加如下实现(目前,这是 shadow DOM 最实用的用法):</p> + +<pre class="brush: js">let shadow = this.attachShadow({mode: 'open'});</pre> + +<p>将 Shadow DOM 附加到一个元素之后,就可以使用 DOM APIs对它进行操作,就和处理常规 DOM 一样。</p> + +<pre class="brush: js">var para = document.createElement('p'); +shadow.appendChild(para); +etc.</pre> + +<h2 id="编写简单示例">编写简单示例</h2> + +<p>现在,让我们着手实现一个示例——<code><a href="https://github.com/mdn/web-components-examples/tree/master/popup-info-box-web-component"><popup-info-box></a></code>(也可以查看<a href="https://mdn.github.io/web-components-examples/popup-info-box-web-component/">在线示例</a>),来说明 Shadow DOM 在 custom element 中的实际运用。它包含一个图片 icon 和一段文字,并将 icon 嵌入页面之中。每当 icon 获取到焦点时,文字会在一个弹框中显示,以提供更加详细的信息。首先,在 JavaScript 文件中,我们需要定义一个叫做 <code>PopUpInfo</code> 的类,它继承自 <code>HTMLElement</code>:</p> + +<pre class="brush: js">class PopUpInfo extends HTMLElement { + constructor() { + // 必须首先调用 super方法 + super(); + + // 元素的具体功能写在下面 + + ... + } +}</pre> + +<p>在上面的类中,我们将会在它的构造函数中定义元素所有的功能。当类实例化后,所有的实例元素都会有相同功能。</p> + +<h3 id="创建_shadow_root">创建 shadow root</h3> + +<p>在构造函数中,我们首先将 Shadow root 附加到 custom element 上:</p> + +<pre class="brush: js">// 创建 shadow root +var shadow = this.attachShadow({mode: 'open'});</pre> + +<h3 class="brush: js" id="创建_shadow_DOM_结构">创建 shadow DOM 结构</h3> + +<p class="brush: js">接下来,我们会使用相关 DOM 操作来创建元素的 Shadow DOM 结构:</p> + +<pre class="brush: js">// 创建 span +var wrapper = document.createElement('span'); +wrapper.setAttribute('class','wrapper'); +var icon = document.createElement('span'); +icon.setAttribute('class','icon'); +icon.setAttribute('tabindex', 0); +var info = document.createElement('span'); +info.setAttribute('class','info'); + +// 获取属性的内容并将内容添加到 info 元素内 +var text = this.getAttribute('text'); +info.textContent = text; + +// 插入 icon +var imgUrl; +if(this.hasAttribute('img')) { + imgUrl = this.getAttribute('img'); +} else { + imgUrl = 'img/default.png'; +} +var img = document.createElement('img'); +img.src = imgUrl; +icon.appendChild(img); +</pre> + +<h3 class="brush: js" id="为_shadow_DOM_添加样式">为 shadow DOM 添加样式</h3> + +<p class="brush: js">之后,我们将要创建 {{htmlelement("style")}} 元素,并加入一些 CSS 样式:</p> + +<pre class="brush: js line-numbers language-js">// 为 shadow DOM 添加一些 CSS 样式 +var style = document.createElement('style'); + +style.textContent = ` +.wrapper { + position: relative; +} + +.info { + font-size: 0.8rem; + width: 200px; + display: inline-block; + border: 1px solid black; + padding: 10px; + background: white; + border-radius: 10px; + opacity: 0; + transition: 0.6s all; + position: absolute; + bottom: 20px; + left: 10px; + z-index: 3; +} + +img { + width: 1.2rem; +} + +.icon:hover + .info, .icon:focus + .info { + opacity: 1; +}`;</pre> + +<h3 id="将_Shadow_DOM_添加到_Shadow_root_上">将 Shadow DOM 添加到 Shadow root 上</h3> + +<p>最后,将所有创建的元素添加到 Shadow root 上:</p> + +<pre class="brush: js">// 将所创建的元素添加到 Shadow DOM 上 +shadow.appendChild(style); +shadow.appendChild(wrapper); +wrapper.appendChild(icon); +wrapper.appendChild(info);</pre> + +<h3 id="使用我们的_custom_element">使用我们的 custom element</h3> + +<p>完成类的定义之后,使用元素也是一样简单,只需将 custom element 放在页面上,正如 <a href="/en-US/docs/Web/Web_Components/Using_custom_elements">Using custom elements</a> 中讲解的那样:</p> + +<pre class="brush: js">// 定义新的元素 +customElements.define('popup-info', PopUpInfo);</pre> + +<pre class="brush: html"><popup-info img="img/alt.png" text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></pre> + +<div> +<div id="tap-translate"></div> +</div> + +<h3 id="使用外部引入的样式">使用外部引入的样式</h3> + +<p>在上面的示例中,我们使用行内{{htmlelement("style")}} 元素为Shadow DOM添加样式,但是完全可以通过{{htmlelement("link")}} 标签引用外部样式表来替代行内样式。</p> + +<p>例如,我们可以看下 <a href="https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/">popup-info-box-external-stylesheet</a> 例子 (查看源代码 <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-external-stylesheet/main.js">source code</a>):</p> + +<pre class="brush: js">// 将外部引用的样式添加到 Shadow DOM 上 +const linkElem = document.createElement('link'); +linkElem.setAttribute('rel', 'stylesheet'); +linkElem.setAttribute('href', 'style.css'); + +// 将所创建的元素添加到 Shadow DOM 上 + +shadow.appendChild(linkElem);</pre> + +<p>请注意, 因为{{htmlelement("link")}} 元素不会打断 shadow root 的绘制, 因此在加载样式表时可能会出现未添加样式内容(FOUC),导致闪烁。</p> + +<p>许多现代浏览器都对从公共节点克隆的或具有相同文本的{{htmlelement("style")}} 标签实现了优化,以允许它们共享单个支持样式表,通过这种优化,外部和内部样式的性能表现比较接近。</p> + +<h2 id="参见">参见</h2> + +<ul> + <li><a href="/zh-CN/docs/Web/Web_Components/Using_custom_elements">使用 custom elements</a></li> + <li><a href="/zh-CN/docs/Web/Web_Components/Using_templates_and_slots">使用 templates 与 slots</a></li> +</ul> diff --git a/files/zh-cn/web/web_components/using_templates_and_slots/index.html b/files/zh-cn/web/web_components/using_templates_and_slots/index.html new file mode 100644 index 0000000000..b0551dcae5 --- /dev/null +++ b/files/zh-cn/web/web_components/using_templates_and_slots/index.html @@ -0,0 +1,334 @@ +--- +title: 使用 templates and slots +slug: Web/Web_Components/Using_templates_and_slots +tags: + - HTML slot + - HTML template + - HTML5 + - Web Components + - shadow dom +translation_of: Web/Web_Components/Using_templates_and_slots +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<p class="summary">这篇文章阐述了如何使用 {{htmlelement("template")}} 和 {{htmlelement("slot")}} 元素创建一个可以用来灵活填充 Web组件的 shadow DOM 的模板。</p> + +<h2 id="关于模板_Templates">关于模板 (Templates)</h2> + +<p>当您必须在网页上重复使用相同的标记结构时,使用某种模板而不是一遍又一遍地重复相同的结构是有意义的。以前这是可行的,但HTML {{htmlelement("template")}} 元素使它更容易实现(这在现代浏览器中得到了很好的支持)。 此元素及其内容不会在DOM中呈现,但仍可使用JavaScript去引用它。</p> + +<p>让我们看一个简单的示例:</p> + +<pre class="brush: html notranslate"><template id="my-paragraph"> + <p>My paragraph</p> +</template></pre> + +<p>上面的代码不会展示在你的页面中,直到你用 JavaScript 获取它的引用,然后添加到DOM中, 如下面的代码:</p> + +<pre class="brush: js notranslate">let template = document.getElementById('my-paragraph'); +let templateContent = template.content; +document.body.appendChild(templateContent);</pre> + +<p>虽然是个简单的例子,但您已经可以开始了解它是多么的有用了。</p> + +<h2 id="在Web_Components中使用模板">在Web Components中使用模板</h2> + +<p>模板(Templates) 本身就是有用的, 而与 web 组件(web component) 一起使用效果更好。我们定义一个 web 组件使用模板作为阴影(shadow) DOM 的内容,叫它 <code><my-paragraph></code>:</p> + +<pre class="brush: js notranslate">customElements.define('my-paragraph', + class extends HTMLElement { + constructor() { + super(); + let template = document.getElementById('my-paragraph'); + let templateContent = template.content; + + const shadowRoot = this.attachShadow({mode: 'open'}) + .appendChild(templateContent.cloneNode(true)); + } +})</pre> + +<p>要注意的关键是我们使用{{domxref("Node.cloneNode()")}} 方法添加了模板的拷贝到阴影的根结点上。</p> + +<p>因为我们添加了模板的内容到 shadow DOM, 所以我们可以加入一些样式信息到模板的 {{htmlelement("style")}} 标签里,这些样式信息稍后会封装到自定义的元素中。如果只给它添加到一个标准的DOM中是不起作用的。</p> + +<p>比如:</p> + +<pre class="brush: html notranslate"><template id="my-paragraph"> + <style> + p { + color: white; + background-color: #666; + padding: 5px; + } + </style> + <p>My paragraph</p> +</template></pre> + +<p>现在我们将它添加自己的 HTML 文档中试试看:</p> + +<pre class="brush: html notranslate"><my-paragraph></my-paragraph> +</pre> + +<div class="note"> +<p><strong>Note</strong>: 模板在浏览器中的支持情况很好, Firefox(版本63及以上),Chrome,Opera ,Edge(版本79及以上) 和 Safari 支持 Shadow DOM</p> +</div> + +<h2 id="使用槽slots_添加灵活度">使用槽(slots) 添加灵活度</h2> + +<p>尽管到这一步已经挺好了,但是元素仍旧不是很灵活. 我们只能在里面放一点文本, 甚至没有普通的 p 标签管用!我们使用 {{htmlelement("slot")}} 让它能在单个实例中通过声明式的语法展示不同的文本。浏览器对这个功能的支持比{{htmlelement("template")}}少,在Chrome 53, Opera 40, Safari 10, 火狐 59 和 Edge 79中支持。</p> + +<p>插槽由其<code>name</code>属性标识,并且允许您在模板中定义占位符,当在标记中使用该元素时,该占位符可以填充所需的任何HTML标记片段。</p> + +<p>如果你想添加一个槽到我们的这个例子,我们会将模板的 p 标签改成下面这样:</p> + +<pre class="brush: html notranslate"><p><slot name="my-text">My default text</slot></p> +</pre> + +<p>如果在标记中包含元素时未定义相关的插槽内容,或者浏览器不支持slot属性,则<code><my-paragraph></code>仅包含后备内容"My default text"。(译者注:此处的意思是使用<my-paragraph>时内部不包裹任何内容时会显示slot 定义好的默认值。具体使用可参考下面)</p> + +<p>要定义插槽内容,我们在<code><my-paragraph></code>元素内包括一个HTML结构,该结构具有{{htmlattrxref("slot")}}属性,其值等于我们要填充的{{htmlelement("slot")}}的name属性的值。 和以前一样,它可以是您喜欢的任何东西,例如:</p> + +<pre class="brush: html notranslate"><my-paragraph> + <span slot="my-text">Let's have some different text!</span> +</my-paragraph></pre> + +<p>或者</p> + +<pre class="brush: html notranslate"><my-paragraph> + <ul slot="my-text"> + <li>Let's have some different text!</li> + <li>In a list!</li> + </ul> +</my-paragraph></pre> + +<p>以上为例。</p> + +<div class="note"> +<p><strong>Note</strong>: 将能被插入到槽中的元素视为 {{domxref("Slotable")}}; 称已经插入到槽中的元素为 <em>slotted</em>.</p> +</div> + +<p>这就我们普通的例子,你想更深入了解你可以 <a href="https://github.com/mdn/web-components-examples/tree/master/simple-template">在GitHub上查看</a> (也可以看 <a href="https://mdn.github.io/web-components-examples/simple-template/">在线运行</a>)。</p> + +<h2 id="更深入的例子">更深入的例子</h2> + +<p>为了更好的结束这篇文章,我们来个看一些不寻常的例子。</p> + +<p>下面的一组代码段展示了我们如何联合使用 <code><slot></code> 和 <code><template></code> 以及一些 JavaScript 去完成:</p> + +<ul> + <li>使用 <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">命名的槽(named slots)</a> 在 <a href="/en-US/docs/Web/API/ShadowRoot">阴影(shadow root)</a> 创建一个 <strong><code><element-details></code></strong> 元素。</li> + <li>将 <strong><code><element-details></code></strong> 元素设计成在文档中使用时, 它将它的 <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a> 中的内容和该元素内部的内容糅合在一起 —也就是元素内容中的片断被填充到元素的 <a href="/en-US/docs/Web/API/ShadowRoot">阴影(shadow root)</a> 的 <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">命名的槽(named slots)</a> 中。</li> +</ul> + +<p>注意,技术上来讲没有<code><template></code> 使用 <code><slot></code> 也是可以的。例如, <code><slot></code> 在一个常规的{{HTMLElement("div")}} 标签里,仍然有占位符的特性,就像在 shadow DOM 中一样,这样我们能避免需要先获取模板对象的 <code>content</code> 属性再使用它的麻烦。然而这个特性在向一个 {{HTMLElement("template")}} 元素中添加槽时更加实用, 因为你不会基于一个渲染的元素定义一个模式。</p> + +<p>另外,就算它还没有渲染,使用{{HTMLElement("template")}}时,作为模板的容器应该语义上应当干净。而且 {{HTMLElement("template")}} 可以直接挂一些对象,如 {{HTMLElement("td")}} ,当加到一个 {{HTMLElement("div")}} 中时会隐藏。</p> + +<div class="note"> +<p><strong>Note</strong>: 查看在完整的例子在 <a href="https://github.com/mdn/web-components-examples/tree/master/element-details">element-details</a> ( <a href="https://mdn.github.io/web-components-examples/element-details/">在线运行</a> ).</p> +</div> + +<h3 id="使用槽slot_创建一个模板">使用槽(slot) 创建一个模板</h3> + +<p>首先,我们在{{HTMLElement("template")}} 使用 <code><slot></code> 标签创建一个"element-details-template"包含一些 <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">命名的槽(named slot)</a> 的 <a href="/en-US/docs/Web/API/DocumentFragment">文档片段(document fragment)</a></p> + +<pre class="brush: html notranslate"><template id="element-details-template"> + <style> + details {font-family: "Open Sans Light",Helvetica,Arial} + .name {font-weight: bold; color: #217ac0; font-size: 120%} + h4 { margin: 10px 0 -8px 0; } + h4 span { background: #217ac0; padding: 2px 6px 2px 6px } + h4 span { border: 1px solid #cee9f9; border-radius: 4px } + h4 span { color: white } + .attributes { margin-left: 22px; font-size: 90% } + .attributes p { margin-left: 16px; font-style: italic } + </style> + <details> + <summary> + <span> + <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code> + <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i> + </span> + </summary> + <div class="attributes"> + <h4><span>Attributes</span></h4> + <slot name="attributes"><p>None</p></slot> + </div> + </details> + <hr> +</template> +</pre> + +<p>上面这个{{HTMLElement("template")}} 有几个特征:</p> + +<ul> + <li>{{HTMLElement("template")}} 标签有一个 {{HTMLElement("style")}} 标签,里面有一些 只能在当前{{HTMLElement("template")}} 的派生中生效的 CSS 样式</li> + <li>{{HTMLElement("template")}} 用 <code><slot></code> 和它的 {{htmlattrxref("name", "slot")}} 属性生成一个 <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">命名的槽(named slots)</a>: + <ul> + <li><code><slot name="element-name"></code></li> + <li><code><slot name="description"></code></li> + <li><code><slot name="attributes"></code></li> + </ul> + </li> + <li>{{HTMLElement("template")}} 包裹 <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">命名的槽(named slots)</a> 在 {{HTMLElement("details")}} 元素中。</li> +</ul> + +<h3 id="从<template>_中创建一个新的_<element-details>_对象">从<template> 中创建一个新的 <element-details> 对象</h3> + +<p>接下来,我们定义一个新的 <strong><code><element-details></code></strong> 元素然后用 {{DOMXref("Element.attachShadow")}} 来将它附着到它的 <a href="/en-US/docs/Web/API/ShadowRoot">阴影(shadow) root</a>, 这个阴影上我们创建了 {{HTMLElement("template")}} 元素。这和我们上面使用的简单例子中的一样。</p> + +<pre class="brush: js notranslate">customElements.define('element-details', + class extends HTMLElement { + constructor() { + super(); + var template = document + .getElementById('element-details-template') + .content; + const shadowRoot = this.attachShadow({mode: 'open'}) + .appendChild(template.cloneNode(true)); + } +}) +</pre> + +<h3 id="结合命名的_slots_使用自定义元素_<element-details>">结合命名的 slots 使用自定义元素 <element-details></h3> + +<p>现在让我们正式在文档流中使用<element-details>标签:</p> + +<pre class="brush: html notranslate"><element-details> + <span slot="element-name">slot</span> + <span slot="description">A placeholder inside a web + component that users can fill with their own markup, + with the effect of composing different DOM trees + together.</span> + <dl slot="attributes"> + <dt>name</dt> + <dd>The name of the slot.</dd> + </dl> +</element-details> + +<element-details> + <span slot="element-name">template</span> + <span slot="description">A mechanism for holding client- + side content that is not to be rendered when a page is + loaded but may subsequently be instantiated during + runtime using JavaScript.</span> +</element-details> +</pre> + +<p>关于以上代码片段,请注意以下几点:</p> + +<ul> + <li>该代码片段有2个 <strong><code><element-details></code> </strong>标签,他们都使用了“slot”属性来引用名为<code>"element-name"</code>和<code>"description"</code>的槽,并把他们都放在根下。</li> + <li>只有第一个 <strong><code><element-details></code></strong> 元素 引用了名为 <code>"attributes"</code> 的槽,而第二个 <code><strong><element-details</strong>></code> 标签没有引用名为<code>"attributes"</code>的槽。</li> + <li>只有第一个 <strong><code><element-details></code> </strong>标签中的"dl"使用了名为 <code>"attributes"</code>的槽,他包含子元素:"dt"和"dd"元素。</li> +</ul> + +<h3 id="添加一些最终的样式">添加一些最终的样式</h3> + +<p>在完成之前,我们在文档中给"dl"和"dt"以及"dd"标签增加一些CSS样式。</p> + +<pre class="brush: css notranslate"> dl { margin-left: 6px; } + dt { font-weight: bold; color: #217ac0; font-size: 110% } + dt { font-family: Consolas, "Liberation Mono", Courier } + dd { margin-left: 16px } +</pre> + +<div class="hidden"> +<pre class="brush: css notranslate">body { margin-top: 47px }</pre> +</div> + +<h3 id="结果">结果</h3> + +<p>最后,让我们所有的代码片段结合在一起,看看渲染的结果是什么样的。</p> + +<p>{{ EmbedLiveSample('full_example', '300','400','https://mdn.mozillademos.org/files/14553/element-details.png','') }}</p> + +<p>关于结果请注意以下几点:</p> + +<ul> + <li>即使 <strong><code><element-details></code> </strong>标签并没有直接使用<code>"details"</code>标签元素,但是在渲染的时候使用了<code>"details"</code>标签,是因为shadow root使得他们被填充。</li> + <li>在渲染出来的<code>"details"</code>标签,<code><strong><element-details></strong></code> 标签从根开始填充了相应的命名的槽。换言之,<strong><code><element-details></code></strong> 的DOM树和shadow root的内容结合在了一起。</li> + <li>对于这两个 <code><strong><element-details></strong></code> 标签,属性标题会自动的从shadow root中添加到名为<code>"attribute"</code>的插槽的前面。</li> + <li>因为第一个 <code><strong><element-details></strong></code> 标签的<code>"dl"</code>元素就明确的引用了名为<code>"attributes"</code>的插槽,所以该<code>"dl"</code>元素的内容将会替换该标签中名为<code>"attributes"</code>的插槽。</li> + <li>因为第二个 <code><strong><element-details></strong></code>标签没有引用名为<code>"attributes"</code> 的槽,所以标签内名为<code>"attributes"的</code>插槽的内容将会使用模板中默认的内容。</li> +</ul> + +<div class="hidden"> +<h5 id="full_example">full example</h5> + +<pre class="brush: html notranslate"><!DOCTYPE html> +<html> + <head> + <title>slot example</title> + <style> + + dl { margin-left: 6px; } + dt { font-weight: bold; color: #217ac0; font-size: 110% } + dt { font-family: Consolas, "Liberation Mono", Courier } + dd { margin-left: 16px } + + </style> + </head> + <body> + <template id="element-details-template"> + <style> + details {font-family: "Open Sans Light",Helvetica,Arial} + .name {font-weight: bold; color: #217ac0; font-size: 120%} + h4 { margin: 10px 0 -8px 0; } + h4 span { background: #217ac0; padding: 2px 6px 2px 6px } + h4 span { border: 1px solid #cee9f9; border-radius: 4px } + h4 span { color: white } + .attributes { margin-left: 22px; font-size: 90% } + .attributes p { margin-left: 16px; font-style: italic } + </style> + <details> + <summary> + <span> + <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code> + <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i> + </span> + </summary> + <div class="attributes"> + <h4><span>Attributes</span></h4> + <slot name="attributes"><p>None</p></slot> + </div> + </details> + <hr> + </template> + + <element-details> + <span slot="element-name">slot</span> + <span slot="description">A placeholder inside a web + component that users can fill with their own markup, + with the effect of composing different DOM trees + together.</span> + <dl slot="attributes"> + <dt>name</dt> + <dd>The name of the slot.</dd> + </dl> + </element-details> + + <element-details> + <span slot="element-name">template</span> + <span slot="description">A mechanism for holding client- + side content that is not to be rendered when a page is + loaded but may subsequently be instantiated during + runtime using JavaScript.</span> + </element-details> + + <script> + customElements.define('element-details', + class extends HTMLElement { + constructor() { + super(); + const template = document + .getElementById('element-details-template') + .content; + const shadowRoot = this.attachShadow({mode: 'open'}) + .appendChild(template.cloneNode(true)); + } + }) + </script> + </body> +</html></pre> +</div> diff --git a/files/zh-cn/web/web_components/影子_dom/index.html b/files/zh-cn/web/web_components/影子_dom/index.html new file mode 100644 index 0000000000..22f4c4c75d --- /dev/null +++ b/files/zh-cn/web/web_components/影子_dom/index.html @@ -0,0 +1,90 @@ +--- +title: 影子DOM(Shadow DOM) +slug: Web/Web_Components/影子_DOM +tags: + - DocumentFragment + - React + - Virtual DOM + - Web Components + - shadow dom +translation_of: Web/Web_Components/Using_shadow_DOM +--- +<p>{{ draft }}</p> + +<p><strong>Shadow DOM </strong>为<a href="/zh-CN/docs/Web/Web_Components">Web组件</a>中的 DOM和 CSS提供了封装。Shadow DOM 使得这些东西与主文档的DOM保持分离。你也可以在一个Web组件外部使用 Shadow DOM本身。</p> + +<p>为什么要把一些代码和网页上其他的代码分离?原因之一是,大型站点若CSS没有良好的组织,导航的样式可能就『泄露』到本不应该去的地方,如主要内容区域,反之亦然。随着站点、应用的拓展,这样的事难以避免。</p> + +<h2 id="Shadow_DOM基础">Shadow DOM基础</h2> + +<p>Shadow DOM 必须附加在一个元素上,可以是HTML文件中的一个元素,也可以是脚本中创建的元素;可以是原生的元素,如<div>、<p>;也可以是<a href="/en-US/docs/Web/Web_Components/Custom_Elements">自定义元素</a>如 <code><my-element>。</code> 如下例所示,使用 {{domxref("Element.<code>attachShadow</code>()")}} 来附加影子DOM:</p> + +<pre class="brush: html"><html> + <head></head> + <body> + <p id="hostElement"></p> + <script> + // create shadow DOM on the <p> element above + var shadow = document.querySelector('#hostElement').<code>attachShadow({mode: 'open'})</code>; + </script> + </body> +</html></pre> + +<p>上例中给一个没有内容的 <p> 元素添加了影子DOM。显示没有变化。接下来,同样在上例中加入下列代码,可以在影子DOM中插入文字,并将在浏览器中显示:</p> + +<pre class="brush: js" style="font-size: 14px;">shadow.innerHTML = '<p>Here is some new text</p>';</pre> + +<h3 id="Shadow_DOM_样式化">Shadow DOM 样式化</h3> + +<p><code><style></code> 元素可用来给影子DOM添加样式。 同样是上例,下列代码会将影子DOM中的文字变为红色:</p> + +<pre class="brush: html"><script> + // 创建 shadow DOM + var shadow = document.querySelector('#hostElement').<code>attachShadow({mode: 'open'})</code>; + // 给 shadow DOM 添加文字 + shadow.innerHTML = '<p>Here is some new text</p>'; + // 添加CSS,将文字变红 + shadow.innerHTML += '<style>p { color: red; }</style>'; +</script> +</pre> + +<h3 id="Shadow_DOM_的组成部分:">Shadow DOM 的组成部分:</h3> + +<p>影子DOM由下列部分组成:</p> + +<ul> + <li>{{domxref("Element.attachShadow()")}}</li> + <li>{{domxref("Element.getDestinationInsertionPoints()")}}</li> + <li>{{domxref("Element.shadowRoot")}}</li> + <li><a href="/en-US/docs/Web/HTML/Element/Content"><content> 元素</a></li> + <li><a href="/en-US/docs/Web/HTML/Element/Shadow"><shadow> 元素</a></li> + <li>这些元素已从规范中移除: <content>, <element> 和<decorator></li> + <li>相关API接口:{{domxref("ShadowRoot")}}, {{domxref("HTMLTemplateElement")}} and {{domxref("HTMLSlotElement")}}</li> + <li>CSS 选择器: + <ul> + <li>伪类:{{cssxref(":host")}}, {{cssxref(":host()")}}, {{cssxref(":host-context()")}}</li> + <li>伪元素:{{cssxref("::shadow")}} and {{cssxref("::content")}}</li> + <li>组合器:<code>>>></code> (formerly <code>/deep/</code>)*</li> + </ul> + </li> +</ul> + +<p>* 将来<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=489954">子组合器有可能被弃用</a></p> + +<h2 id="Interfaces">Interfaces</h2> + +<p>{{domxref("ShadowRoot")}}</p> + +<p>DOM子树的根节点,和文档的主要DOM树分开渲染。</p> + +<p>{{domxref("HTMLTemplateElement")}}</p> + +<p>允许访问HTML元素的内容。</p> + +<p>{{domxref("HTMLSlotElement")}}</p> + +<p>定义一个槽的位置。</p> + +<p>{{domxref("DocumentOrShadowRoot")}}</p> + +<p>提供在文档和 Shadow 树之间共享的API。</p> |
