使用JavaScript注入HTML的最佳方法是什么?

33

我希望这不是太主观了。我认为有一个明确的答案,所以我来问了。

我想使用JS(没有库)动态创建这个html:

<a href="#" id="playButton">Play</a>
<a href="javascript: void(0)" id="muteUnmute">Mute</a>
<div id="progressBarOuter"> 
  <div id="bytesLoaded"></div>
    <div id="progressBar"></div>
</div>
<div id="currentTime">0:00</div>
<div id="totalTime">0:00</div>

使用JavaScript。我知道我可以使用createElement等来实现,但为每个元素执行此操作似乎非常冗长。有人能建议一种更简洁的方法吗。

在这个项目中,我没有访问库...所以没有jQuery等。


8
“我知道我可以使用createElement等方式来完成这个任务,但是对于每个元素都这样做似乎非常冗长。”——是的。当你有重复且冗长的编程任务时,你可以通过将冗长的部分封装在一个函数中来自动化它。当你有一些经常使用的这些函数时,你可以将它们组合起来,然后嘣——你就有了一个库!所以,现在是时候开始编写你自己的库了。 - Paul D. Waite
Gen Covid-19只有一种跟进的答案。Word. - brasofilo
10个回答

32

让你的标记和代码分离:

您可以在 HTML 页面中嵌入将用作隐藏模板的 HTML 片段,并根据需要进行克隆:

<style type="text/css">
#templates { display: none }
</style>
...
<script type="text/javascript">
var node = document.getElementById("tmp_audio").cloneNode(true);
node.id = ""; // Don't forget :)
// modify node contents with DOM manipulation
container.appendChild(node);
</script>
...
<div id="templates">
    <div id="tmp_audio">
        <a href="#" class="playButton">Play</a>
        <a href="#" class="muteUnmute">Mute</a>
        <div class="progressBarOuter"> 
            <div class="bytesLoaded"></div>
            <div class="progressBar"></div>
        </div>
        <div class="currentTime">0:00</div>
        <div class="totalTime">0:00</div>
    </div>
</div>

更新:请注意,我已经将模板中的 id 属性转换为 class 属性。这样可以避免在页面上有多个具有相同 id 的元素。您可能甚至不需要使用这些类。您可以通过以下方式访问元素:

node.getElementsByTagName("div")[4].innerHTML =
    format(data.currentTime);

或者,您可以对模板的HTML进行操作:

<script type="text/javascript">
var tmp = document.getElementById("tmp_audio").innerHTML;
// modify template HTML with token replacement
container.innerHTML += tmp;
</script>

2
以这种方式操作会创建具有相同ID的多个元素。 - Ryan Ternier
5
不会。请注意 node.id = ""; 这一行。 - Ates Goral
1
@Ryan Ternier:啊,我明白了。你说的不是模板的ID,而是其中的ID。只是我复制粘贴了OP的模板。通常情况下,模板内部不会有ID。我会编辑我的答案。 - Ates Goral
@AtesGoral:最近的浏览器不解析注入的innerHTML(http://meta.stackexchange.com/q/257243/242800)。 - user2284570
1
我看过KendoUI库将整个内容包装在<script type="text/x-kendo-template">some_html_content</script>中。在这种情况下,模板HTML将不会与复制的代码共享相同的ID。 - Eric Lam
显示剩余2条评论

31

将整个内容都塞进一个JS变量中:

var html = '<a href="#" id="playButton">Play</a>';
html += '<a href="javascript: void(0)" id="muteUnmute">Mute</a>';
html += '<div id="progressBarOuter"><div id="bytesLoaded"></div><div id="progressBar"></div></div>';
html += '<div id="currentTime">0:00</div>';
html += '<div id="totalTime">0:00</div>';

那么:

document.getElementById("parentElement").innerHTML = html;

如果你想要这样做:

document.getElementById("totalTime").innerHTML = "5:00";

2
这不是最好的解决方案,但考虑到条件,它是可以接受的。 - Ryan Ternier
我想我会选择这个。 - Mike Rifgin
我使用数组来实现: var html = []; html.push('<div>'); html.push('</div>')... htmlString = html.join('')... - Denis
4
最近的浏览器不会解析注入的innerHTML。 - user2284570
1
@user2284570 浏览器不解析textContent,但会解析innerHTML。这就是为什么它被称为innerHTML的原因。 - ADJenks
我有点不安,因为没有任何答案或评论提到DOMParser,这是一种官方API,用于将XML或HTML源代码解析为DOM文档,比innerHTML赋值更明确和有意义。(避免使用该赋值语句反序列化HTML的历史原因主要涉及旧版浏览器的内存管理问题,或者说缺乏内存管理。即使在IE 9上也支持该赋值语句。) - amcgregor

13

如果您生活在2019年及以后,请阅读此处。

使用JavaScript es6,您可以使用字符串文字创建模板。

创建一个返回字符串/模板文字的函数。

function videoPlayerTemplate(data) {
    return `
        <h1>${data.header}</h1>
        <p>${data.subheader}</p>
        <a href="#" id="playButton">Play</a>
        <a href="javascript: void(0)" id="muteUnmute">Mute</a>
        <div id="progressBarOuter"> 
            <div id="bytesLoaded"></div>
            <div id="progressBar"></div>
        </div>
        <time id="currentTime">0:00</time>
        <time id="totalTime">0:00</time>
    `
}

创建一个JSON对象,其中包含您想要显示的数据。
var data = {
     header: 'My video player',
     subheader: 'Version 2 coming soon'
}

将其添加到您喜欢的任何元素中

const videoplayer = videoPlayerTemplate(data);
document.getElementById('myRandomElement').insertAdjacentHTML("afterbegin", videoplayer);

关于字符串字面量的更多信息可以在这里阅读。


1
这是一个可爱的开场白。你在 Stack Overflow 疫情前和疫情后都获胜了! - brasofilo

9

您可以使用

<script type="text/javascript">
    function appendHTML() {
        var wrapper = document.createElement("div");
        wrapper.innerHTML = '\
<a href="#" id="playButton">Play</a>\
<a href="javascript: void(0)" id="muteUnmute">Mute</a>\
<div id="progressBarOuter"> \
<div id="bytesLoaded"></div>\
    <div id="progressBar"></div>\
</div>\
<div id="currentTime">0:00</div>\
<div id="totalTime">0:00</div>\
';
        document.body.appendChild(wrapper);
    }
</script>

6

编辑:HTML导入现在已经弃用

现在使用Web Components,你可以使用HTML导入来注入HTML。

语法看起来像这样:

<link rel="import" href="component.html" >

这将仅按顺序内联加载href属性中html文件的内容。您可以在加载的文件中包含任何有效的html,因此即使想要加载其他脚本也可以。
要从JavaScript注入它,您可以执行以下操作:
var importTag = document.createElement('link');
importTag.setAttribute('rel', 'import');
importTag.setAttribute('href', 'component.html');
document.body.appendChild(importTag);

我写下这篇文章的时候,Chrome和Opera已经支持HTML导入。您可以在此处查看最新兼容性表 http://caniuse.com/#feat=imports

但是不用担心浏览器不支持它,您仍然可以使用webcomponentsjs填充来在浏览器中使用它。

有关HTML导入的更多信息,请查看http://webcomponents.org/articles/introduction-to-html-imports/


1

如果您不需要对语法进行任何验证(这就是使createElement()如此好用的原因),那么您可以始终将{{link1:innerHTML}}属性设置为要插入标记的元素内部。

个人而言,我会坚持使用createElement()。虽然它更冗长,但这样可以减少很多需要担心的事情。


0

这里有两种可能的情况:

  1. 您的HTML是静态的
  2. 您的HTML是动态的

解决方案1

在这种情况下,将您的HTML用双引号包装起来,将其变成字符串并保存在一个变量中。然后将其推入HTML中,这里是一个演示:

HTML

<div id="test"></div>

JavaScript

let selector = document.querySelector("#test");

let demo_1 = "<div id='child'> hello and welcome</div>"
selector.innerHTML = demo_1;

解决方案 2

在这种情况下,将您的HTML代码用反引号包装起来,将其变成模板字面量并保存在一个变量中。然后将其推入HTML中,在此过程中,您可以使用变量来更改内容。以下是演示:

HTML

<div id="test"></div>

JavaScript

let selector = document.querySelector("#test");

let changes = 'hello and welcome'
let demo_1 = `<div id='child'>${changes}</div>`
selector.innerHTML = demo_1;

0
如果性能是一个问题,应该避免使用innerHTML。您应该使用document.createElement()创建整个对象树,包括嵌套元素,需要多少次就创建多少次。
最后,用一条语句将其附加到文档中,而不是多条语句。
在我多年的非正式测试中,这将为您提供最佳性能(某些浏览器可能会有所不同)。
如果HTML在变量中声明,它应该是简单的,并且是为了特定的目的。通常,这不是正确的方法。

迭代式的createElement与简单的批量反序列化相比,在内存受限设备(如移动设备)上表现非常低效。请考虑:在最终用户可用之前,您的数据必须采取多少形式?数据库序列化、JSON、JS内存中、HTML DOM等等。HTML是一种数据模型,并且可以用这种方式使用 - amcgregor

-1

我有一个情况,我将文本传递给第三方库,但如果我的模型是私有的,我想在文本中添加一个元素。

return { id: item.id, text: (item.isPrivate == true) ? "<i class=\"icon-lock\" title=\"Private group.\"></i> " + item.label : item.label };

这会导致第三方库构建其标记的问题。

这绝不是一个好主意,但第三方库存在的目的是为了让我们不必自己编写所有内容。在这种情况下,您必须依赖通过JavaScript传递标记。

当我找到一个合适的解决方案时,我会给你更新。


还有一个标准库。除非你喜欢左填充(或是负零,或是数组等)灾难,否则不要总是去寻找第三方解决方案,特别是对于这样基本的操作。 - amcgregor

-1

您可以连接原始的HTML字符串(注意转义文本并防止XSS漏洞),或者您可以重写jQuery(或类似的东西)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接