142

我有一个正在循环的数组。每次条件为真时,我都想将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
    });
});
4

8 回答 8

218

老问题,但由于问题询问“使用 jQuery”,我想我会提供一个选项,让您在不引入任何供应商依赖性的情况下执行此操作。

虽然有很多模板引擎,但它们的许多功能最近都变得不受欢迎,迭代<% for(现代模板实践鼓励简单地将对象映射到它的 DOM(或其他)表示,例如,我们看到的属性映射到 ReactJS 中的组件(尤其是无状态组件)。<% if<%= myString | uppercase %>

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

请注意,使用我们的令牌,您会以交替[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 - 您应该能够使用带有document.querySelectorand的 vanilla javascript 采用相同的方法.innerHTML

jsfiddle

JS里面的模板

要问自己的一个问题是:您真的想要/需要将模板定义为 HTML 文件吗?您总是可以组件化 + 重用模板,就像重用大多数要重复的东西一样:使用函数。

在 es7-land 中,使用解构、模板字符串和箭头函数,您可以编写非常漂亮的组件函数,可以使用上述$.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(然后运行脚本等)。

于 2016-08-21T14:12:52.523 回答
174

您可以决定在项目中使用模板引擎,例如:

如果您不想包含另一个库,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);
});
于 2013-09-07T13:59:18.310 回答
53

改用 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 <模板>

  • +它的内容在被激活之前实际上是惰性的。本质上,您的标记是隐藏的 DOM,不会呈现。

  • +模板中的任何内容都不会产生副作用。脚本不运行,图像不加载,音频不播放......直到使用模板。

  • +内容被认为不在文档中。在主页中使用document.getElementById()or不会返回模板的子节点。querySelector()

  • +模板可以放置在 、 或 内的任何位置<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默认有。
  • +惰性 - 浏览器不会将脚本内容解析为 JS,因为它的类型设置为"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

创建 Web 组件使用 Trawersy Media 的 HTML 模板创建自定义 Web 组件教程: https ://youtu.be/PCWaFLy3VUo

于 2017-10-12T01:10:00.733 回答
13

在正文中的某处添加

<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() );
于 2013-09-07T13:40:08.150 回答
3

为了解决这个问题,我认出了两种解决方案:

  • 第一个与 AJAX 一起使用,您必须使用它从另一个文件加载模板,并在每次需要时添加.clone().

    $.get('url/to/template', function(data) {
        temp = data
        $('.search').keyup(function() {
            $('.list-items').html(null);
    
            $.each(items, function(index) {
                 $(this).append(temp.clone())
            });
    
        });
    });
    

    考虑到应该在 ajax 完成后添加事件以确保数据可用!

  • 第二种是直接将它添加到原始 html 中的任何位置,选择它并将其隐藏在 jQuery 中:

    temp = $('.list_group_item').hide()
    

    您可以在添加模板的新实例后使用

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
    
  • 与上一个相同,但是如果您不希望模板保留在那里,而只是在 javascript 中,我认为您可以使用(尚未测试过!).detach()而不是隐藏。

    temp = $('.list_group_item').detach()
    

    .detach()从 DOM 中删除元素,同时保持数据和事件处于活动状态(.remove() 不会!)。

于 2013-09-07T13:41:12.217 回答
3

其他选择:

我使用它,它对我帮助很大。他们网站上显示的示例:

HTML

<div class="who">
</div>

JSON

{
  "who": "Hello Wrrrld"
}

结果

<div class="who">
  Hello Wrrrld
</div>
于 2014-09-12T20:45:27.353 回答
2

DevWL关于使用原生 HTML5模板元素的很好回答。为了对 OP 提出的这个好问题做出贡献,我想补充一下如何使用 jQuery 来使用这个元素,例如:template

$($('template').html()).insertAfter( $('#somewhere_else') );

的内容template不是html,只是作为数据处理,所以你需要将内容包装到一个jQuery对象中,然后才能访问jQuery的方法。

于 2020-06-11T02:18:46.323 回答
0

以下是如何<template>更有效地使用元素和 jQuery,因为在撰写本文时其他 jQuery 回答使用.html()强制 HTML 被序列化和重新解析。

首先,以模板为例:

<template id="example"><div class="item">
    <h1 class="title"></h1>
    <p class="description"></p>
</div></template>

<template>请注意,标签与其内容之间没有空格,内容结尾和</template>. 如果你把空间放在那里,它也会被复制。

现在,JS:

// example data
const items = [
    {name: "Some item", description: "Some description"},
    // ...
];

const template = $("template#example").contents();
const templateTitle = template.find("h1.title");
const templateDescription = template.find("p.description");
const target = $("body");

for (const item of items) {
    // fill in the placeholders
    templateTitle.text(item.name);
    templateDescription.text(item.description);
    // copy the template and add it to the page
    target.append(template.clone());
}

这将为 items 数组中的每个项目添加模板的副本,并替换占位符。请注意,几个元素,如templatetarget,只被检索一次。

于 2021-07-11T01:54:43.050 回答