javascript - 使用 jQuery 定義要附加的 HTML 範本
我有一個陣列正在迴圈處理。每當某個條件成立時,我想將下面的 HTML 代碼副本附加到一個容器元素中,並帶有一些值。
我可以把這個 HTML 放在哪裡以智能的方式重複使用?
<a href="#" class="list-group-item">
<div class="image">
<img src="" />
</div>
<p class="list-group-item-text"></p>
</a>
jQuery
$('.search').keyup(function() {
$('.list-items').html(null);
$.each(items, function(index) {
// APPENDING CODE HERE
});
});
答案#1
舊問題,但因為問題是問「使用 jQuery」,我想我提供一個選項,讓你在不引入任何廠商依賴的情況下做到這一點。
雖然有很多模板引擎存在,但最近它們的許多功能已不再受到青睞,迭代(<% for)、條件(<% if)和轉換(<%= myString | uppercase %>)頂多被視為微語言,最壞的情況則是反模式。現代的模板實踐鼓勵簡單地將一個對象映射到其 DOM(或其他)表示,例如在 ReactJS 中看到的將屬性映射到組件(尤其是無狀態組件)的方法。
HTML 內的模板
你可以依靠的一個特性,是將你的模板 HTML 保持在其他 HTML 旁邊的方法,是使用非執行的 <script> type,例如 <script type="text/template">。對你的情況:
<script type="text/template" data-template="listitem">
<a href="${url}" class="list-group-item">
<table>
<tr>
<td><img src="${img}"></td>
<td><p class="list-group-item-text">${title}</p></td>
</tr>
</table>
</a>
</script>
在文件加載時,讀取您的模板並使用簡單的 String#split 將其分詞
var itemTpl = $('script[data-template="listitem"]').text().split(/\$\{(.+?)\}/g);
注意,使用我們的 token,你會得到交替的 [text, property, text, property] 格式。這讓我們可以使用 Array#map 很好地映射它,配合一個映射函數:
function render(props) {
return function(tok, i) { return (i % 2) ? props[tok] : tok; };
}
其中 props 可能看起來像 { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }。
將所有內容綜合起來,假設你已經如上解析並加載了你的 itemTpl,並且你有一個在範圍內的 items 陣列:
$('.search').keyup(function () {
$('.list-items').append(items.map(function (item) {
return itemTpl.map(render(item)).join('');
}));
});
這種方法也勉強算是 jQuery - 你應該能夠使用原生 JavaScript,搭配 document.querySelector 和 .innerHTML 採取相同的方法。
jsfiddle
JS 內的模板
問自己的一個問題是:你真的想要/需要將模板定義為 HTML 文件嗎?你總是可以將模板組件化並重複使用,就像你重複使用大多數想要重複的東西一樣:使用函數。
在 ES7 的世界中,使用解構賦值、模板字串和箭頭函數,你可以編寫看起來相當漂亮的組件函數,並且可以使用上面提到的 $.fn.html 方法輕鬆加載。
const Item = ({ url, img, title }) => `
<a href="${url}" class="list-group-item">
<div class="image">
<img src="${img}" />
</div>
<p class="list-group-item-text">${title}</p>
</a>
`;
那麼你甚至可以輕鬆地渲染它,即使是從陣列映射過來,也可以這樣做:
$('.list-items').html([
{ url: '/foo', img: 'foo.png', title: 'Foo item' },
{ url: '/bar', img: 'bar.png', title: 'Bar item' },
].map(Item).join(''));
哦,最後一點提醒:不要忘記對傳給模板的屬性進行清理,特別是當它們是從資料庫讀取時,否則有人可能會從你的頁面傳入 HTML(然後執行腳本等)。
答案#2
你可以決定在你的專案中使用模板引擎,例如:
- 鬍鬚
- underscore.js
- 車把手
如果你不想加入另一個函式庫,John Resig 提供了一個 jQuery 解決方案,類似下面的這個。
瀏覽器和螢幕閱讀器會忽略未識別的腳本類型:
<script id="hidden-template" type="text/x-custom-template">
<tr>
<td>Foo</td>
<td>Bar</td>
<tr>
</script>
使用 jQuery,根據模板添加行會類似於:
var template = $('#hidden-template').html();
$('button.addRow').click(function() {
$('#targetTable').append(template);
});
答案#3
改用 HTML 範本吧!
由於所接受的答案將代表過載腳本方法,我想建議另一個方法,依我看,由於過載腳本存在的 XSS 風險,這個方法更加乾淨且更安全。
我做了一個示範,向你展示如何在操作中使用它,以及如何將一個模板注入到另一個模板中,進行編輯,然後添加到文件的 DOM 中。
範例 HTML
<template id="mytemplate">
<style>
.image{
width: 100%;
height: auto;
}
</style>
<a href="#" class="list-group-item">
<div class="image">
<img src="" />
</div>
<p class="list-group-item-text"></p>
</a>
</template>
範例 js
// select
var t = document.querySelector('#mytemplate');
// set
t.content.querySelector('img').src = 'demo.png';
t.content.querySelector('p').textContent= 'demo text';
// add to document DOM
var clone = document.importNode(t.content, true); // where true means deep copy
document.body.appendChild(clone);
HTML <template>
+它的內容在被啟用之前實際上是惰性的。本質上,你的標記是隱藏的 DOM,並不渲染。
+模板中的任何內容都不會有副作用。腳本不會運行,圖片不會加載,音頻不會播放……直到該模板被使用。
+內容被視為不在文件中。在主頁面中使用
document.getElementById()或querySelector()不會返回模板的子節點。+Templates 可以放置在
<head>、<body>或<frameset>的任何位置,並且可以包含這些元素允許的任何類型內容。請注意,「任何位置」的意思是<template>可以安全地用在 HTML 解析器不允許的地方。
後退
瀏覽器支援應該不成問題,但如果你想涵蓋所有可能性,你可以進行一個簡單的檢查:
要檢測
<template>的功能,請創建 DOM 元素並檢查 .content 屬性是否存在:
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// Good to go!
} else {
// Use old templating techniques or libraries.
}
關於重載腳本方法的一些見解
- +沒有內容被呈現 —— 瀏覽器不會呈現這個區塊,因為
<script>標籤預設為display:none。 - +不活躍 - 瀏覽器不會將腳本內容解析為 JavaScript,因為其類型被設置為除
"text/javascript"之外的其他類型。 - - 安全性問題 - 鼓勵使用
.innerHTML。對使用者提供的資料進行執行時字串解析,容易導致 XSS 漏洞。
完整文章:https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old
有用的參考資料: https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector
建立網頁元件 使用 HTML 模板建立自訂網頁元件的教學,由 Trawersy Media 提供: https://youtu.be/PCWaFLy3VUo
答案#4
在身體的某個地方添加
<div class="hide">
<a href="#" class="list-group-item">
<table>
<tr>
<td><img src=""></td>
<td><p class="list-group-item-text"></p></td>
</tr>
</table>
</a>
</div>
然後建立 CSS
.hide { display: none; }
並將其添加到你的 js
$('#output').append( $('.hide').html() );
