参赛作品
给同事的问候。 本文是有关Web组件的系列文章中的第三篇也是最后一篇文章,前两篇文章位于:
Web组件。 第1部分:自定义元素Web组件 第2部分:影子DOM本文将讨论<template>元素以及HTML导入。
HTML模板元素
<template>元素是一个工具,它允许您在客户端上存储内容而不将其呈现到页面上,但是能够在通过JavaScript执行期间显示它。
解析页面时,仅根据内容验证来处理元素的内容,但不对其进行呈现(根据规范,呈现时此元素不代表任何内容)。 元素的内容可以从脚本克隆并粘贴到文档中,脚本可以独立用于标准化和创建Web组件时使用。
内容<模板>
<template>的内容以及不具有浏览器上下文的任何节点的内容均不适用任何合规性要求,但HTML和XML语法的正确性要求除外。 这意味着,例如,在模板的内容中,您可以指定img元素而无需指定src和alt属性的值,因此:
<template> <div> <img src="{{src}}" alt="{{alt}}"> </div> </template>
但是,在<template>元素之外,这种语法无效。 在这种情况下,跳过结束标记</ div>将违反HTML语法,并且对于<template>的内容无效。
在html代码的<template>标记内指定的所有元素都不是其子元素。
浏览器在创建<template>元素时会创建一个DocumentFragment,其文档即所谓的 批准由该算法确定的模板内容所有者文档,该文档中指定了<template>并指示DocumentFragment创建的.content属性的值。
也就是说,template元素的.content属性包含一个DocumentFragment,在<template>标记内的html代码中指定的元素是此DocumentFragment的子元素。
在这种情况下,可以将<template>元素与其他元素一样添加子元素( appendChild() ),但这将被视为违反了模板内容模型。
克隆模板
克隆模板的内容时,请务必记住.cloneNode的第一个参数([deep])
或必须导入.importNode中的第二个(externalNode,深) (根据规范,如果未传递参数,则不应进一步执行)。
顺便说一句,是的,尽管大多数示例都使用.cloneNode(),但也可以使用.importNode()。 唯一的区别是文档更新时(对于.cloneNode()-在调用appendChild()之后;对于.importNode()-克隆之后)。
显示代码©
使用模板真的非常简单。 我将继续使用该选项卡的组件示例,并使用前几篇文章中的示例代码 。
我将从在html标记中创建两个<template>元素开始,并在其中传输位于TabNavigationItem和TabContentItem类的.render()方法中的标记(我也更改了一些样式,但这并不影响功能):
<template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a> </template>
和:
<template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div> </template>
在每个类的构造函数中,我将保存template属性。 对于TabNavigationItem,这将是:
this.template = document.getElementById('tab-nav');
TabContentItem的一个:
this.template = document.getElementById('tab-content');
在每个类的render()方法中,删除.innerHTML条目后,我将添加以下代码:
const content = this.template.content.cloneNode(true); this.shadowRoot.appendChild(content);
结果代码可以在这里找到。
在此示例中,两个模板均以html格式指定,这看起来很麻烦且不会引起嗡嗡声。 这使我们顺利进入主题:
HTML导入
导入是HTML文档,它们通过另一个HTML文档作为外部资源连接。 规范草案中对文档之间的关系系统进行了很好的描述,而不是本文的主题。
常规方案在图像中可见:

。
为了实现导入,在HTML链接类型(rel属性的值)中添加了新类型。
在<link>元素本身的rel属性值中指定的导入单词会创建一个到导入资源的链接(资源的默认类型是text / html)。
<link>元素可能具有async属性。
HTMLLinkElement API提供了规范草案提供的扩展:添加了一个包含导入文档的只读import属性。
在两种情况下,属性可以包含null:<link>不代表导入或<link>不在文档中时。
规范单独声明应始终返回同一对象。
在导入的上下文中,有一个所谓的主文档,即同时导入资源而不是别人的导入资源的文档。
此类文档的ContentSecurityPolicy应该限制所有导入。 因此,如果将“内容安全性标题字段”设置为导入,则浏览器必须对导入的文档强制执行主文档的策略。
在实践中
对于选项卡组件,我创建一个模板文件夹。 在其中,我将创建两个文件,在其中传输组件标记。
<template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div> </template> <template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a> </template>
在index.html文件的<head>中,导入模板:
<link rel="import" href="templates/tab-nav.html" id="tab-nav"> <link rel="import" href="templates/tab-content.html" id="tab-content">
我将id属性添加到<link>元素,因为我需要从js访问它们。
现在,在TabNavigationItem和TabContentItem类的构造函数中,要获取模板文档,我只需要找到对应的<link>元素并转到其import属性,然后我将搜索已存在于导入文档中的模板:
class TabNavigationItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({mode: 'open'}); const templateImport = document.getElementById('tab-nav').import; this.template = templateImport.getElementById('tab-nav'); }
最终版本可以在这里获取 。
关于支持
支持HTML模板:Edge c 16,Firefox c 59,Chrome c 49,Safari c 11。
使用进口支持支架:Chrome c 49。
因此,本文中的示例只能在最新的Chrome中看到。
有polyphiles:
Web组件聚合物项目阅读有关模板和导入的更多信息:
HTML规范HTML5规范HTML导入规范草案仅此而已,谢谢收看,
谭雅