翻译正在进行中。

这篇文章阐述了如何使用 <template><slot> 元素创建一个灵活的模板,然后可以用来填充填一个 Web组件的 shadow DOM。

关于模板的事实

当您必须在网页上重复使用相同的标记结构时,使用某种模板而不是一遍又一遍地重复相同的结构是有意义的。  以前这是可能的,但HTML <template> 元素使它更容易实现(这在现代浏览器中得到了很好的支持)。 此元素及其内容不会在DOM中呈现,但仍可使用JavaScript引用它。

让我们看一个简单的快速示例:

<template id="my-paragraph">
  <p>My paragraph</p>
</template>

上面的代码不会展示在你的页面中,直到你用 JavaScript 获取它的引用,然后添加(append) 到DOM中, 如下面的代码:

let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);

很简单,但是你已经看到了该如何使用了。

和Web Components一起使用模板

模板(Templates) 本身是有用的,  但是与 web 组件(web component) 一起使用效果加好.  定义一个 web 组件使用模板作为阴影(shadow)DOM 的内容. 我们叫它 <my-paragraph>:

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(temComponentsplateContent.cloneNode(true));
  }
})

要注意的关键是我们使用Node.cloneNode() 方法添加了模板(template) 的拷贝到阴影(shadow) 的根结点上.

因为我们添加了模板的内容了到阴影(shadow) DOM, 我们可以加入一些样式信息到模板(template)的 <style> 标签里, 稍后封装到自定义的元素中. 如果只给它添加到一个标准的DOM是不起作用的

比如:

<template id="my-paragraph">
  <style>
    p {
      color: white;
      background-color: #666;
      padding: 5px;
    }
  </style>
  <p>My paragraph</p>
</template>

现在我们将它添加自己的 HTML 档案中试试看:

<my-paragraph></my-paragraph>

Note: 虽然模板(templates) 在浏览器中很好的支持了, 但是 Shadow DOM 还没有.  Shadow DOM 在 Chrome 和 Opera已经支持了. 火狐也快了(注:翻译的时候已经支持了);  将火狐的preferences(地址栏输入 about:config) 的dom.webcomponents.enabled 设为 true. 火狐的实现将在60/61版本开启. Safari 已经支持 shadow DOM了, Edge也正在努力.

使用槽(slot) 添加灵活度

顺利到这一步,  元素不是很灵活. 我们只能在里面放一点文本, 甚至没有普通的 p标签管用! 我们使用 <slot> 让它能在单个实例中通过声明式的语法展示不同的文本. 这个功能比 <template>支持的更少, 在Chrome 53, Opera 40, Safari 10, 火狐 59 中支持, Edge不支持.

槽(slot) 使用 name 属性作为id, 你能在模板(tempalte) 中定义任意的 html 片断作为占位符,在模板对象被使用在html 脚本中时.

如果你想添加一个槽(slot) 到这个普通的例子,我们将模板(tempalte) 的 p 标签改成下面这样:

<p><slot name="my-text">My default text</slot></p>

如果模板对象添加到html 脚本中时槽(slot)的内容还没定义, 或者浏览器不支持槽(slot) , <my-paragraph>  将显示默认的 "My default text".

要定义槽(slot) 的内容, 我们添加一段含有slot 属性的 html结构到 <my-paragraph> 元素中,slot 属性的值就是任何你想填的槽的名字

<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>

或者

<my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>

以上为例.

Note: 将能被插入到槽(slot)中的元素视为 Slotable; 称已经插入到slot 中的元素叫 slotted.

这就我们普通的例子,你想更深入了解你可以 在GitHub上查看 (也可以看 在线运行).

更深入的例子

为了了结这篇文章,我们来个看一个不简单的.

下面的一组代码集展示我们如何联合使用 <slot><template> 以及一些 JavaScript来

注意,技术上讲没有<template> 使用 <slot> 也是可以的, 例如.,  <slot> 在一个常规的<div> 标签里, 仍然有占位符的特性, 就你在阴影(Shadow)DOM 中一样, 这样我们能避免需要先获取模板对象的 content 属性再使用它的麻烦. 然而这个特性在向一个 <template> 元素中添加槽时更加实用, 因为你不会基于一个渲染的元素定义一个模式.

另外,就算它还没有渲染,使用<template>时,作为模板的容器应该语义上应当干净. 而且 <template>   可以直接挂一些对象, 如  <td> ,当加到一个 <div> 中时会隐藏.

Note: 查看在完整的例子在 element-details ( 在线运行 ).

使用槽(slot) 创建一个模板

首先,我们在<template> 使用  <slot> 标签创建一个"element-details-template"包含一些 命名的槽(named slot)  的 文档片断(document fragment)

<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>

上面这个<template> 有几个特征

从<template> 中创建一个新的 <element-details> 对象

接下来,我们定义一个新的 <element-details> 元素然后用 Element.attachShadow 来将它附着到它的 阴影(shadow) root, 这个阴影上我们创建了 <template> 元素. 这和我们上面使用的简单例子中的一样.

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));
  }
})

结合命名的 slots 使用自定义元素 <element-details>

现在让我们正式在文档流中的使用<element-details>标签:

<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> 

关于以上代码片段,请注意以下几点:

  • 该代码片段有2个 <element-details> 标签,他们都使用了“slot”属性来引用名为"element-name""description"的槽,并把他们都放在根下。
  • 只有第一个 <element-details> 元素 引用了名为 "attributes" 的槽,而第二个 <element-details> 标签没有引用名为"attributes"的槽。
  • 只有第一个 <element-details> 标签中的"dl"使用了名为 "attributes"的槽,他包含子元素:"dt"和"dd"元素。

添加一些最终的样式

在完成之前,我们在文档中给"dl"和"dt"以及"dd"标签增加一些CSS样式。

  dl { margin-left: 6px; }
  dt { font-weight: bold; color: #217ac0; font-size: 110% }
  dt { font-family: Consolas, "Liberation Mono", Courier }
  dd { margin-left: 16px }

结果

最后,让我们所有的代码片段结合在一起,看看渲染的结果是什么样的。

ScreenshotLive sample

关于结果请注意以下几点:

  • 即使 <element-details> 标签并没有直接使用"details"标签元素,但是在渲染的时候使用了"details"标签,是因为shadow root使得他们被填充。
  • 在渲染出来的"details"标签,<element-details> 标签从根开始填充了相应命名的槽(slots)。换言之,<element-details> 的DOM树和shadow root的内容结合在了一起。
  • 对于这两个 <element-details> 标签,属性标题会自动的从shadow root中添加到名为"attribute"的插槽(slot)的前面。
  • 因为第一个 <element-details> 标签的"dl"元素从根就明确的引用了名为"attributes"的插槽,所以该"dl"标签的内容将会被该标签中名为"attribute"的插槽代替。
  • 因为第二个 <element-details>标签没有引用名为"attributes" 的槽,所以他的内容中该名为"attributes"槽的内容将会使用模板中默认的内容填充。

文档标签和贡献者

此页面的贡献者: JasonKee, xgqfrms, mdnwebdocs-bot, xrr2016, zxk7516
最后编辑者: JasonKee,