時(shí)間:2023-06-13 06:33:02 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-06-13 06:33:02 來(lái)源:網(wǎng)站運(yùn)營(yíng)
【翻譯】創(chuàng)建可重用的 HTML 模板:本文翻譯自 Crafting Reusable HTML Templates | CSS-Tricks在上篇文章中,我們從高層次討論了 Web 組件規(guī)范(自定義元素,影子 DOM,以及 HTML 模板)。在本文以及接下來(lái)的三篇文章中,我們將測(cè)試這些技術(shù),并對(duì)它們進(jìn)行更詳細(xì)的研究,看看如何在現(xiàn)代生成中使用它們。為此,我們將從零開(kāi)始構(gòu)建一個(gè)自定義模態(tài)對(duì)話(huà)框,看看各種技術(shù)是如何結(jié)合到一起的。
作者:Caleb Williams
發(fā)布日期:2019-03-19
更新日期:2019-03-22
文章系列:
第一部分:Web 組件介紹(也就是本文)
第二部分:創(chuàng)建可重用的 HTML 模板
第三部分:從零開(kāi)始創(chuàng)建自定義元素
第四部分:用影子 DOM 封裝樣式和結(jié)構(gòu)
第五部分:Web 組件的高級(jí)工具
<template>
元素。在本系列第一篇文章中,我們將模板元素定義為“HTML 中直到調(diào)用才會(huì)渲染的用戶(hù)自定義模板”。換句話(huà)說(shuō),模板是被瀏覽器忽略直到被告知的 HTML 代碼。<template>
就是 HTML 元素,因此包含內(nèi)容的模板的最簡(jiǎn)單形式是:<template> <h1>Hello world</h1></template>
在瀏覽器中執(zhí)行上面這段代碼只會(huì)看到一個(gè)空白屏幕,因?yàn)闉g覽器不會(huì)呈現(xiàn)模板元素的內(nèi)容。這種機(jī)制非常強(qiáng)大,因?yàn)樗试S我們定義內(nèi)容(或內(nèi)容結(jié)構(gòu))并保存以備將來(lái)使用——而不是在 JavaScript 中編寫(xiě) HTML 代碼。const template = document.querySelector('template');const node = document.importNode(template.content, true);document.body.appendChild(node);
真正的魔法發(fā)生在 document.importNode
方法中。該方法將創(chuàng)建模板內(nèi)容的一個(gè)副本,并準(zhǔn)備將其插入到另一個(gè)文檔(或文檔片段)中。函數(shù)的第一個(gè)參數(shù)獲取模板的內(nèi)容,第二參數(shù)告訴瀏覽器對(duì)元素的 DOM 子樹(shù)(即其所有子樹(shù))進(jìn)行深度賦值。template.content
,但這樣做會(huì)將移除元素的內(nèi)容并以后將其添加到文檔的 body 中。任何 DOM 節(jié)點(diǎn)都只能在一個(gè)位置連接,所以后續(xù)使用模板的內(nèi)容將導(dǎo)致一個(gè)空的文檔片段(本質(zhì)上是空值),因?yàn)閮?nèi)容先前已經(jīng)被移動(dòng)。使用 documnet.importNode
方法允許我們?cè)诙鄠€(gè)位置重用相同的模板內(nèi)容。document.body
中,并呈現(xiàn)給用戶(hù)。這最終允許我們做一些有趣的事情,比如為用戶(hù)(或我們程序的使用者)提供創(chuàng)建內(nèi)容的模板,類(lèi)似于下面的 demo,我們?cè)谏弦黄恼轮刑岬竭^(guò):<template id="book-template"> <li><span class="title"></span> — <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');
在本例中,我們提供了兩個(gè)模板來(lái)呈現(xiàn)相同的內(nèi)容——作者和他們所寫(xiě)的書(shū)籍。當(dāng)表單更改時(shí),我們選擇呈現(xiàn)與該值關(guān)聯(lián)的模板。我們最終會(huì)使用這種技術(shù)創(chuàng)建一個(gè)自定義元素,該元素將使用稍后定義的模板。<button id="click-me">Log click event</button>
給他添加點(diǎn)樣式:button { all: unset; background: tomato; border: 0; border-radius: 4px; color: white; font-family: Helvetica; font-size: 1.5rem; padding: .5rem 1rem;}
再搞個(gè)好簡(jiǎn)單的腳本調(diào)用它:const button = document.getElementById('click-me');button.addEventListener('click', event => alert(event));
當(dāng)然,我們可以使用 HTML 的 <style>
和 <script>
標(biāo)簽將這些代碼之間放到一個(gè)模板中,而不是放到多個(gè)分開(kāi)的文件中:<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,我們就有了一個(gè)新按鈕,其 ID 為 #click-me
,一個(gè)全局的 CSS 選擇器目標(biāo)時(shí)按鈕的 ID,以及一個(gè)簡(jiǎn)單的事件監(jiān)聽(tīng)器,它將提醒元素的 click 事件。document.inportNode
附加內(nèi)容,并且我們有一個(gè)主要包含 HTML 的模板,可以在頁(yè)面之間移動(dòng)。'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">✖️</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>
這段代碼將作為我們對(duì)話(huà)框的基礎(chǔ)。簡(jiǎn)單的分析一下,我們有一個(gè)全局關(guān)閉按鈕、一個(gè)標(biāo)題、一些內(nèi)容。 我們還添加了一些行為來(lái)直觀(guān)地切換對(duì)話(huà)框(雖然目前還不可用)。#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));
結(jié)果:文章系列:
第一部分:Web 組件介紹(也就是本文)
第二部分:創(chuàng)建可重用的 HTML 模板
第三部分:從零開(kāi)始創(chuàng)建自定義元素
第四部分:用影子 DOM 封裝樣式和結(jié)構(gòu)
第五部分:Web 組件的高級(jí)工具
Laxman March 19, 2019
我最近不在模板里的<style>
標(biāo)簽中直接寫(xiě)樣式了,而是使用<style>@import '/path/to/component.css';</style>
。
這樣,我可以輕松的維護(hù)樣式,并且還可以從中包含其他樣式,比如您想要添加的普通全局樣式,它們不能直接穿影子 DOM。
Caleb Williams March 20, 2019
Hey Laxman,這是一個(gè)完全合法的策略,如果你絕對(duì)確定你知道引用樣式的路徑,這種策略非常有意義。 然而,對(duì)于本文示例,我希望確保涵蓋最基本的內(nèi)容,而不深入了解導(dǎo)入樣式的最佳方式(這點(diǎn)將在影子 DOM 文章中詳細(xì)介紹)。
Glenn March 19, 2019
模板中的樣式和腳本元素是否以任何方式限定了作用范圍?或者他們只是作為一個(gè)整體附加到整個(gè)文檔中,前者遵循樣式的常規(guī)級(jí)聯(lián)規(guī)則,后者插入到相同的 JavaScript 命名空間中?如果沒(méi)有作用域限制,那腳本中的函數(shù)將在每次使用模板時(shí)復(fù)制一次,并且每次都會(huì)覆蓋上一個(gè)副本的名稱(chēng)?而樣式將在級(jí)聯(lián)中不停堆疊?這兩種機(jī)制都不會(huì)使用多實(shí)例模板中的樣式和腳本有啥吸引力。
Caleb Williams March 20, 2019
Hey Glenn,多謝提問(wèn)。不對(duì),模板中的樣式和腳本不受范圍限制,所以使用這種使用元素的方式并不是一個(gè)很好的策略。模板節(jié)點(diǎn)實(shí)際上更多用于 HTML,而不是樣式或腳本,本文我僅僅是想證明它們可以以這種方式使用,盡管這種方式不一定好。在后續(xù)的兩篇文章中,我們將討論如何利用自定義元素和影子 DOM 來(lái)進(jìn)一步優(yōu)化此代碼。
Konstantin March 20, 2019
不錯(cuò)的系列。我真的很喜歡你可以盡可能簡(jiǎn)短地解釋 Web 組件,但又不遺漏主要內(nèi)容。但有件事很困擾我。您使用document.importNode()
而不是Node.cloneNode()
(例如,fragment.content.cloneNode(true);
)有什么特殊原因嗎?
請(qǐng)務(wù)必回復(fù)。
Caleb Williams March 20, 2019
這兩者之間并沒(méi)有太大的區(qū)別。如果我沒(méi)記錯(cuò)的話(huà),我認(rèn)為如果文檔不同,使用cloneNode
將隱含地采用節(jié)點(diǎn)(它們可能是模板節(jié)點(diǎn)是文檔片段)。所以在本例中,document.importNode
更顯示。
Andrew April 20, 2019
我在前端開(kāi)發(fā)中遇到一個(gè)小問(wèn)題 & 我正在使用 HTML/CSS 以及 bootstrap 框架構(gòu)建自己的網(wǎng)站。我想做的是將我的主頁(yè)元素(包括頂部導(dǎo)航欄、頁(yè)腳等)設(shè)置為模板,并在每個(gè)頁(yè)面上調(diào)用它們。Web 組件是實(shí)現(xiàn)這一點(diǎn)的好方法嗎?或者是否存在一種更簡(jiǎn)單/更少步驟的方法實(shí)現(xiàn)這一點(diǎn)(要用 JavaScript 也沒(méi)關(guān)系)?
關(guān)鍵詞:模板,創(chuàng)建,翻譯
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。