定义要使用JQuery追加的HTML模板


125

我有一个要遍历的数组。每当条件为真时,我想将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
    });
});

2
如果您正在寻找一种聪明的方法,请将所有信息放入一个DIV中并设置样式。不要只用两个单元格创建大量表,也不要将它们包装到锚点中。
谢尔盖·斯涅吉列夫2013年

3
OP显然对适当的编码实践(赞!)感兴趣,因此我想提供帮助。如果我想为实际问题做出贡献,我会发布一个答案。我敢肯定,任何人都同意,使用一个成熟的两单元格表格放置图像和一些文本几乎是没有道理的,除了一些真正具有异国情调的要求(IE4吗?)
Sergey Snegirev

1
@BrianG。发表评论并不能真正暗示您要切线。尽管我同意评论是适合这些评论的场所(只要它们仍然有意义),但它们也适合那些尚无时间将其扩展为答案的人的开车提示。明确说明您在做什么对OP很有帮助。
millimoose 2013年

没人回答你的问题,帕特里克?
塞巴斯蒂安·内拉,2013年

Answers:


164

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

如果您不想包含另一个库,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);
});

@MaksimLuzik是正确的。小胡子(注意商标拼写)重量较轻,可以插入OP的代码中,但是Handlebars具有更多处理数组的功能,最终可能提供了更充分的解决方案。这是一篇不错的比较文章:blog.cubettech.com/…–
Michael Scheper

13
此处如何编辑模板的示例:jsfiddle.net/meehanman/7vw8bc84
Dean Meehan

175

旧问题,但是由于该问题询问“使用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);

请注意,有了我们的令牌,您将获得交替[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.querySelector和的原始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(然后运行脚本等),请不要忘记清理传递给模板的属性。


1
好的模板引擎。
亚瑟·卡斯特罗

40
您的JS部分中的模板是炸弹
BigRon

2
您能为“ HTML内的模板”方法添加一个演示/提琴吗?
LCJ

1
哇!JS中的模板...没想到!惊人!
罗曼·洛佩兹

2
哇,这真的很好。我正在向每个模板添加一个data-url属性。并为每个获取ajax数据。真好,节省了我很多时间!谢谢。
戴维

42

请改用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()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
  • + Inert-浏览器不会将脚本内容解析为JS,因为其类型设置为"text/javascript"
  • -安全问题-鼓励使用.innerHTML。用户提供的数据的运行时字符串分析很容易导致XSS漏洞。

全文:https : //www.html5rocks.com/zh-CN/tutorials/webcomponents/template/#toc-old

有用的参考:https : //developer.mozilla.org/zh-CN/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector

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


4
templateInternet Explorer不支持该元素(自2018年起,IE11)。使用w3c.github.io/html/single-page.html的示例580进行了测试
罗兰

我喜欢<template>,尽管我建议您自由使用类,所以很明显什么道具被设置为什么。仍然需要一些js技能才能将数据正确映射到模板中,尤其是使用组件样式的方法来映射大型数据集。就我个人而言,我仍然喜欢仅在函数中构建HTML,或者理想情况下直接使用JS本身直接构建DOM并完全放弃HTML(例如React如何做到)。
来自Qaribou的Josh,18年

模板方法对我来说效果很好。我的一个建议是首先克隆模板(而不是最后),然后使用克隆的对象。否则,您正在对模板本身进行更改。如果您有条件逻辑,仅在某些时间设置一些属性/内容,则这些属性将在您下次使用模板时出现。通过在每次使用前进行克隆,可以确保始终有一个统一的工作基础。
jt。

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

1
OP具有JSON,并希望使用该数组作为数据源来呈现一些html。这个答案如何解决这个问题?您的答案使用一个html节点并对其进行克隆/复制,而与数据绑定无关。
Adrian Iftode 2013年

所以我是谁的人。
Adrian Iftode 2013年

我真的认为您无助于获得更好的答案,因为您可能会从提醒我们开始一点,即我们的答案所针对的范围更广。
塞巴斯蒂安·内拉,2013年

1
确实可以,但是.children().clone()比做起来更有效.html()。对于<tr/>使用此代码的页面,随机访问的速度约为3:1 i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time。该比率实际上有点夸张,因为我使用的是$ a.clone(),但是尝试将其清空每次迭代都比克隆更能提高性能,所以我不确定如何使其更准确,因为计时功能要自己付费。
Chinoto Vokro,2016年

1
这个方法是不好的。主要是因为即使不使用模板内容也可以下载并解析。这是一个太大的问题,无法将其视为有效答案。同样,正如上面提到的其他人一样,如果您必须至少使用克隆代替.html()
DevWL,2017年

3

其他选择:

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

的HTML

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

JSON格式

{
  "who": "Hello Wrrrld"
}

结果

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

2

为了解决此问题,我认识两种解决方案:

  • 第一个与AJAX一起使用,您必须使用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()不能!)。


-没问题!-–
iConnor

该操作具有JSON,并希望使用该数组作为数据源来呈现一些html。这个答案如何解决这个问题?您的答案使用一个html节点并对其进行克隆/复制,而与数据绑定无关。
Adrian Iftode 2013年

1
我引用op:我想将下面的HTML代码副本附加到具有某些值的容器元素上。 我相信这解决了他的问题。
塞巴斯蒂安·内拉2013年

以及如何处理“某些价值”部分?
Adrian Iftode 2013年

好吧,这并不意味着它不能解决问题。非常感谢您告诉我我遗漏了以下几点之一:)无论如何,如果我什至不知道我们在说什么值,我怎么知道如何插入这些值?只需看一下这一部分,我可以哪里将该HTML巧妙地重用?。真正在问什么?关于JSON或HTML包含?
塞巴斯蒂安·内拉2013年

1

来自DevWL的关于使用本机HTML5 模板元素的很好的答案。为了从OP中回答这个好问题,我想补充一下如何使用jQuery 使用此元素,例如:template

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

的内容template不是html,而是作为数据处理,因此您需要将内容包装到jQuery对象中,然后才能访问jQuery的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.