15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 【翻译】创建可重用的 HTML 模板

【翻译】创建可重用的 HTML 模板

时间:2023-06-13 06:33:02 | 来源:网站运营

时间:2023-06-13 06:33:02 来源:网站运营

【翻译】创建可重用的 HTML 模板:

本文翻译自 Crafting Reusable HTML Templates | CSS-Tricks
作者:Caleb Williams
发布日期:2019-03-19
更新日期:2019-03-22
在上篇文章中,我们从高层次讨论了 Web 组件规范(自定义元素,影子 DOM,以及 HTML 模板)。在本文以及接下来的三篇文章中,我们将测试这些技术,并对它们进行更详细的研究,看看如何在现代生成中使用它们。为此,我们将从零开始构建一个自定义模态对话框,看看各种技术是如何结合到一起的。

文章系列:
第一部分:Web 组件介绍(也就是本文)
第二部分:创建可重用的 HTML 模板
第三部分:从零开始创建自定义元素
第四部分:用影子 DOM 封装样式和结构
第五部分:Web 组件的高级工具

HTML 模板

Web 组件规范 中最不被认可但功能最强大的特性之一是 <template> 元素。在本系列第一篇文章中,我们将模板元素定义为“HTML 中直到调用才会渲染的用户自定义模板”。换句话说,模板是被浏览器忽略直到被告知的 HTML 代码。

这些模板可以通过很多有趣的方式传递和重用。本文中,我们将研究创建一个对话框模板,该对话框最终会在自定义元素中使用。

定义我们的模板

和听起来一样简单,<template> 就是 HTML 元素,因此包含内容的模板的最简单形式是:

<template> <h1>Hello world</h1></template>在浏览器中执行上面这段代码只会看到一个空白屏幕,因为浏览器不会呈现模板元素的内容。这种机制非常强大,因为它允许我们定义内容(或内容结构)并保存以备将来使用——而不是在 JavaScript 中编写 HTML 代码。

为了使用模板,我们需要 JavaScript

const template = document.querySelector('template');const node = document.importNode(template.content, true);document.body.appendChild(node);真正的魔法发生在 document.importNode 方法中。该方法将创建模板内容的一个副本,并准备将其插入到另一个文档(或文档片段)中。函数的第一个参数获取模板的内容,第二参数告诉浏览器对元素的 DOM 子树(即其所有子树)进行深度赋值。

我们本可以直接使用 template.content,但这样做会将移除元素的内容并以后将其添加到文档的 body 中。任何 DOM 节点都只能在一个位置连接,所以后续使用模板的内容将导致一个空的文档片段(本质上是空值),因为内容先前已经被移动。使用 documnet.importNode 方法允许我们在多个位置重用相同的模板内容。

然后该节点被附加到 document.body 中,并呈现给用户。这最终允许我们做一些有趣的事情,比如为用户(或我们程序的使用者)提供创建内容的模板,类似于下面的 demo,我们在上一篇文章中提到过:

<template id="book-template"> <li><span class="title"></span> &mdash; <span class="author"></span></li></template><template id="book-template-2"> <li><span class="author"></span>'s classic novel <span class="title"></span></li></template><ul id="books"></ul><fieldset id="templates"> <legend>Choose template</legend> <label> <input type="radio" name="template" value="book-template" checked> Template One </label> <label> <input type="radio" name="template" value="book-template-2"> Template Two </label></fieldset>label { display: block; margin-bottom: 0.5rem;}'use strict';const books = [ { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' }, { title: 'A Farewell to Arms', author: 'Ernest Hemingway' }, { title: 'Catch 22', author: 'Joseph Heller' }];function appendBooks(templateId) { const booksList = document.getElementById('books'); const fragment = document.getElementById(templateId); // Clear out the content from the ul booksList.innerHTML = ''; // Loop over the books and modify the given template books.forEach(book => { // Create an instance of the template content const instance = document.importNode(fragment.content, true); // Add relevant content to the template instance.querySelector('.title').innerHTML = book.title; instance.querySelector('.author').innerHTML = book.author; // Append the instance ot the DOM booksList.appendChild(instance); }); }document.getElementById('templates').addEventListener('change', (event) => appendBooks(event.target.value));appendBooks('book-template');在本例中,我们提供了两个模板来呈现相同的内容——作者和他们所写的书籍。当表单更改时,我们选择呈现与该值关联的模板。我们最终会使用这种技术创建一个自定义元素,该元素将使用稍后定义的模板。

模板的通用性

模板的一个有趣之处是,它可以包含任何 HTML 代码。包括 script 和 style 元素。举一个非常简单的例子,假设有个模板,它附加了一个按钮,当点击按钮时会弹出一个提醒。

<button id="click-me">Log click event</button>给他添加点样式:

button { all: unset; background: tomato; border: 0; border-radius: 4px; color: white; font-family: Helvetica; font-size: 1.5rem; padding: .5rem 1rem;}再搞个好简单的脚本调用它:

const button = document.getElementById('click-me');button.addEventListener('click', event => alert(event));当然,我们可以使用 HTML 的 <style><script> 标签将这些代码之间放到一个模板中,而不是放到多个分开的文件中:

<template id="template"> <script> const button = document.getElementById('click-me'); button.addEventListener('click', event => alert(event)); </script> <style> #click-me { all: unset; background: tomato; border: 0; border-radius: 4px; color: white; font-family: Helvetica; font-size: 1.5rem; padding: .5rem 1rem; } </style> <button id="click-me">Log click event</button></template>一旦元素被添加到 DOM,我们就有了一个新按钮,其 ID 为 #click-me,一个全局的 CSS 选择器目标时按钮的 ID,以及一个简单的事件监听器,它将提醒元素的 click 事件。

对于我们的脚本,我们简单地使用 document.inportNode 附加内容,并且我们有一个主要包含 HTML 的模板,可以在页面之间移动。

'use strict';const template = document.getElementById('template');document.body.appendChild( document.importNode(template.content, true));

为我们的对话框创建模板

回到我们制作对话框元素的任务,我们想要定义模板的内容和样式。

<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'); closeButton.focus(); closeButton.addEventListener('click', () => { wrapper.classList.remove('open'); wasFocused.focus(); }); }); </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%; position: fixed; top: 0; right: 0; bottom: 0; left: 0; width: 100%; } .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">&#x2716;&#xfe0f;</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>这段代码将作为我们对话框的基础。简单的分析一下,我们有一个全局关闭按钮、一个标题、一些内容。 我们还添加了一些行为来直观地切换对话框(虽然目前还不可用)。

遗憾的是,样式和脚本内容不于局限于我们的模板,而是应用到整个文档,在有多个模板实例被添加到 DOM 中时,会导致不理想的行为。在我们下一篇文章中,我们将用到自定义元素,创建一个我们自己实时使用此模板并封装元素行为的元素。

完整示例还包含以下 CSS 和 JS 代码:

#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('dialog-template');document.body.appendChild( document.importNode(template.content, true));结果:

点击前:

点击后:

文章系列:
第一部分:Web 组件介绍(也就是本文)
第二部分:创建可重用的 HTML 模板
第三部分:从零开始创建自定义元素
第四部分:用影子 DOM 封装样式和结构
第五部分:Web 组件的高级工具

译者精选评论翻译

Laxman March 19, 2019
我最近不在模板里的 <style> 标签中直接写样式了,而是使用 <style>@import '/path/to/component.css';</style>
这样,我可以轻松的维护样式,并且还可以从中包含其他样式,比如您想要添加的普通全局样式,它们不能直接穿影子 DOM。
Caleb Williams March 20, 2019
Hey Laxman,这是一个完全合法的策略,如果你绝对确定你知道引用样式的路径,这种策略非常有意义。 然而,对于本文示例,我希望确保涵盖最基本的内容,而不深入了解导入样式的最佳方式(这点将在影子 DOM 文章中详细介绍)。

Glenn March 19, 2019
模板中的样式和脚本元素是否以任何方式限定了作用范围?或者他们只是作为一个整体附加到整个文档中,前者遵循样式的常规级联规则,后者插入到相同的 JavaScript 命名空间中?如果没有作用域限制,那脚本中的函数将在每次使用模板时复制一次,并且每次都会覆盖上一个副本的名称?而样式将在级联中不停堆叠?这两种机制都不会使用多实例模板中的样式和脚本有啥吸引力。
Caleb Williams March 20, 2019
Hey Glenn,多谢提问。不对,模板中的样式和脚本不受范围限制,所以使用这种使用元素的方式并不是一个很好的策略。模板节点实际上更多用于 HTML,而不是样式或脚本,本文我仅仅是想证明它们可以以这种方式使用,尽管这种方式不一定好。在后续的两篇文章中,我们将讨论如何利用自定义元素和影子 DOM 来进一步优化此代码。

Konstantin March 20, 2019
不错的系列。我真的很喜欢你可以尽可能简短地解释 Web 组件,但又不遗漏主要内容。但有件事很困扰我。您使用 document.importNode() 而不是 Node.cloneNode()(例如,fragment.content.cloneNode(true);)有什么特殊原因吗?
请务必回复。
Caleb Williams March 20, 2019
这两者之间并没有太大的区别。如果我没记错的话,我认为如果文档不同,使用 cloneNode 将隐含地采用节点(它们可能是模板节点是文档片段)。所以在本例中,document.importNode 更显示。

Andrew April 20, 2019
我在前端开发中遇到一个小问题 & 我正在使用 HTML/CSS 以及 bootstrap 框架构建自己的网站。我想做的是将我的主页元素(包括顶部导航栏、页脚等)设置为模板,并在每个页面上调用它们。Web 组件是实现这一点的好方法吗?或者是否存在一种更简单/更少步骤的方法实现这一点(要用 JavaScript 也没关系)?

关键词:模板,创建,翻译

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭