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

IT技术 javascript jquery json
2021-03-12 22:06:51

我有一个我正在循环的数组。每次条件为真时,我都想将HTML以下代码的副本附加到具有某些值的容器元素。

我可以把这个 HTML 放在哪里以聪明的方式重用?

<a href="#" class="list-group-item">
    <div class="image">
         <img src="" />
    </div>
    <p class="list-group-item-text"></p>
</a>

查询

$('.search').keyup(function() {
    $('.list-items').html(null);

    $.each(items, function(index) {
        // APPENDING CODE HERE
    });
});
6个回答

老问题,但由于问题询问“使用 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和 的vanilla javascript 来采用相同的方法.innerHTML

提琴手

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(然后运行脚本等)。

哇,这真是太好了。我正在为每个模板添加一个 data-url 属性。并为每个获取 ajax 数据。这真是太好了,为我节省了很多时间!谢谢你。
2021-04-28 22:06:51
哇!JS里面的模板……没想到!惊人!
2021-05-06 22:06:51
不错的模板引擎。
2021-05-07 22:06:51
你的JS模板内的部分是炸弹
2021-05-12 22:06:51
您能否为“HTML 中的模板”方法添加一个演示/小提琴?
2021-05-14 22:06:51

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

如果您不想包含另一个库,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 是对的。Mustache(注意商标拼写)重量更轻,会插入到 OP 的代码中,但 Handlebars 具有更多处理数组的功能,最终可能会提供更充分的解决方案。这是一篇不错的比较文章:blog.cubettech.com/...
2021-05-03 22:06:51
如何在此处编辑模板的示例:jsfiddle.net/meehanman/7vw8bc84
2021-05-03 22:06:51

请改用 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/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

我喜欢<template>,虽然我建议自由地使用类,所以很清楚什么 props 被设置为什么。将您的数据正确映射到模板中仍然需要一些 js 技能,尤其是使用组件样式的方法来映射大型数据集。就我个人而言,我仍然喜欢在函数中构建 HTML,或者理想情况下直接使用 JS 本身构建 DOM 而完全放弃 HTML(例如 React 是如何做到的)。
2021-04-15 22:06:51
模板方法对我来说效果很好。我的一个建议是首先克隆模板(而不是最后)并使用克隆的对象。否则,您所做的更改将发生在模板本身上。如果您有条件逻辑,您只在某些时候设置某些属性/内容,那么这些属性将在您下次使用模板时出现。通过在每次使用前进行克隆,您可以确保始终拥有一致的工作基础。
2021-04-22 22:06:51
templateInternet Explorer 不支持元素(截至 2018 年,IE11)。使用w3c.github.io/html/single-page.html 的示例 580 进行测试
2021-04-25 22:06:51

在正文中添加某处

<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() );
OP 有一个 JSON,并希望使用该数组作为数据源呈现一些 html。这个答案是如何解决问题的?您的答案需要一个 html 节点并克隆/复制它,而与数据绑定无关。
2021-04-27 22:06:51
我真的认为您并没有帮助获得更好的答案,因为您可以开始提醒我们,这一点比我们的答案所解决的问题更广泛。
2021-04-30 22:06:51
这种做法很糟糕。主要是因为即使您没有使用它,您也会下载并解析模板内容。这是一个太大的问题,无法将其视为有效答案。同样作为上面提到的其他人,如果您必须至少使用 clone 而不是 .html()
2021-04-30 22:06:51
所以我是谁在投票。
2021-05-06 22:06:51
这确实有效,但 do.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(),但是每次迭代尝试清空它比克隆更影响性能,所以我不确定如何使它更准确,因为计时功能有其自身的成本。
2021-05-09 22:06:51

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

  • 第一个与 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() 没有!)。

嗯,这并不意味着它不解决问题。我很感激你告诉我我错过了其中的一点 :) 无论如何,如果我什至不知道我们在谈论什么值,我怎么知道如何插入这些值看看这部分我可以把这个 HTML 放到哪里以聪明的方式重用?. 真正被问到的是什么?关于 JSON 或 HTML 包含?
2021-04-16 22:06:51
——没问题!——
2021-04-20 22:06:51
op 有一个 JSON 并希望使用该数组作为数据源呈现一些 html。这个答案是如何解决问题的?您的答案需要一个 html 节点并克隆/复制它,而与数据绑定无关。
2021-04-20 22:06:51
我引用 op:我想将下面的 HTML 代码的副本附加到具有某些值的容器元素。 我相信这解决了他的问题。
2021-04-26 22:06:51
以及如何处理“某些值”部分?
2021-05-02 22:06:51