[JavaScript] Templates 的應用

我是一個很懶又有點潔癖的 dev, 能不用套件就不用套件, 能使用 plain native language 就不去用其它花俏的招式。但這個習慣每次在維護他人專案時就往往破功。HTML5 的 template 功能已經不算是新東西了, 但當我不得不用時, 我憑著印象寫出來的程式卻錯了, 只好心不甘情不願地回頭把文件重新查一遍。這篇就算是我的筆記吧!

<template> 這個 HTML5 的 tag 對我這種 back-end 開發者可能比較少用到。畢竟, 我們可以使用後端語言去組出同樣的內容, 何必多此一舉到前端網頁去寫 template 呢?

但是當我們必須去寫前端程式而不打算依賴後端時, HTML5 的 element 就變成一個省力的工具了。

Last thing first

先來看範例程式的結果好了:

圖一. 輸出結果

 

 

 

 

 

 

 

圖一就是我們最後看到的樣子。它的來源資料如下:

const arr = [
  { 'name': 'Johnny', 'birthYear': 2001, 'role': 'Manager' },
  { 'name': 'Grace', 'birthYear': 2000, 'role': 'Staff' },
  { 'name': 'Steve', 'birthYear': 1993, 'role': 'VP' },
  { 'name': 'Jeff', 'birthYear': 1999, 'role': 'Staff' },
  { 'name': 'Barbie', 'birthYear': 2002, 'role': 'GM' }
];

至於用到的 HTML 碼則只有聊聊幾行:

<ol id="placeholder">
</ol>
<template id="liStaff">
  <li class="record">
    <b><span>Span1</span></b><br/>
    <span>Span2</span>
  </li>
</template>

如果不套用 JavaScript 指令的話, 畫面上是全空的。template 這個 tag 裡面的任何文字都不會顯示。

檢查相容性

我實在很不想提, 但是老問題仍然存在, 那就是 IE11 (及以下)的相容性。

據我查到的結果, IE11 應該是支援 HTML5 Template 及 Content 的。但事實上偏偏不是那回事。所以我們不能免俗地必須排除掉這種情況, 並提供適當的警告文字:

if ("content" in document.createElement("template")) {
	...
} else {
  console.warn('This browser does\'nt support HTML Templates! '); // For IE11
}

操作 template

對於 template 的操作十分直覺, 我看就不必花太多篇幅解釋了:

// 程式一
if ("content" in document.createElement("template")) {
  thisYear = (new Date()).getFullYear();
  arr.forEach(item => {
    const clone = liStaff.content.cloneNode(true);
    const span = clone.querySelectorAll('span');
    span[0].textContent = item.name + ' (' + (thisYear-item.birthYear) + ')';
    span[1].textContent = item.role;
    placeholder.appendChild(clone);
  });
} else {
  console.warn('This browser does\'nt support HTML Templates! '); // For IE11
}

基本上, 就是透過 cloneNote 指令把 templete 中的 content 元素複製起來, 然後把文字一個一個填進去, 再透過 appendChild 指令加入 DOM 就可以了。

加碼放送 Template Literals

既然講到了 Template, 不如再雞婆地加碼介紹 Template Literals 好了 (雖然這兩者還真的沒有半點關係):

// 程式二
if ("content" in document.createElement("template")) {
  thisYear = (new Date()).getFullYear();
  arr.forEach(item => {
    const clone = liStaff.content.cloneNode(true);
    const span = clone.querySelectorAll('span');
    span[0].textContent = `${item.name} (${thisYear-item.birthYear})`;
    span[1].textContent = `${item.role}`;
    placeholder.appendChild(clone);
  });
} else {
  console.warn('This browser does\'nt support HTML Templates! '); // For IE11
}

以上兩個程式的輸出結果是一模一樣的, 差別在程式二中使用了稱為 Tagged template literals 的寫法。這是在 ES6 中加入的功能。但其實也不能算「新」了。

請注意在 `${item.name} (${thisYear-item.birthYear})``${item.role}`這兩行中使用了倒引號, 而不是普通的單引號。使用倒引號括住的字串仍然是字串, 但是它可以跨行引用, 不必再使用加號把它們連起來。這在許多情況下可以給我們帶來一些好處。可惜的是, 由於我們現在是在寫 HTML, 這些跨行的文字在 HTML 中仍然會通通擠在同一行, 所以在這裡對我們用處並不大。

但是我們可以在 Tagged template literals 中直接使用變數名稱, 還可以 inline 加入指令, 就像程式二中的寫法。所以它實際上還是可以幫我們省一點小小的力氣。不妨把它記起來, 遲早用得到的。


Dev 2Share @ 點部落