網站搜尋

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() );