我有一个表示元素的HTML字符串:'<li>text</li>'
。我想将其附加到DOM中的一个元素(ul
以我为例)。如何使用Prototype或DOM方法做到这一点?
(我知道我可以在jQuery中轻松地做到这一点,但不幸的是我们没有使用jQuery。)
var nodes = document.fromString("<b>Hello</b> <br>");
我有一个表示元素的HTML字符串:'<li>text</li>'
。我想将其附加到DOM中的一个元素(ul
以我为例)。如何使用Prototype或DOM方法做到这一点?
(我知道我可以在jQuery中轻松地做到这一点,但不幸的是我们没有使用jQuery。)
var nodes = document.fromString("<b>Hello</b> <br>");
Answers:
注意:当前大多数浏览器都支持HTML <template>
元素,HTML 元素提供了一种更可靠的方式来从字符串创建元素。有关详细信息,请参见下面的Mark Amery的答案。
对于较旧的浏览器和node / jsdom:(<template>
在撰写本文时尚不支持元素),请使用以下方法。库用于从HTML字符串中获取DOM元素的操作是相同的(为IE实现,还需要一些额外的工作来解决IE的错误innerHTML
):
function createElementFromHTML(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
// Change this to div.childNodes to support multiple top-level nodes
return div.firstChild;
}
请注意,与HTML模板不同,这不适用于某些不能合法地作为a的子元素的元素<div>
,例如<td>
s。
如果您已经在使用库,建议您坚持使用库批准的从HTML字符串创建元素的方法:
update()
方法中内置了此功能。jQuery(html)
和jQuery.parseHTML
方法中实现了它。createElementFromHTML
具有误导性,因为div.firstChild
返回的a Node
不是HTMLElement
eg can node.setAttribute
。从函数创建Element
返回div.firstElementChild
。
<div>
包装我添加的HTML .innerHTML
令人讨厌。我从来没有想过使用.firstChild
。
div
,并且输出是[object SVGSVGElement]
在控制台日志为我提供正确的DOM元素的同时。我究竟做错了什么?
HTML 5引入了<template>
可用于此目的的元素(如WhatWG规范和MDN文档中所述)。
甲<template>
元件用于声明可以在脚本中使用的HTML的片段。元素在DOM中表示为HTMLTemplateElement
,其.content
属性为DocumentFragment
类型,以提供对模板内容的访问。这意味着,你可以通过设置一个HTML字符串转换为DOM元素innerHTML
a的<template>
元素,然后伸到template
的.content
财产。
例子:
/**
* @param {String} HTML representing a single element
* @return {Element}
*/
function htmlToElement(html) {
var template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
}
var td = htmlToElement('<td>foo</td>'),
div = htmlToElement('<div><span>nested</span> <span>stuff</span></div>');
/**
* @param {String} HTML representing any number of sibling elements
* @return {NodeList}
*/
function htmlToElements(html) {
var template = document.createElement('template');
template.innerHTML = html;
return template.content.childNodes;
}
var rows = htmlToElements('<tr><td>foo</td></tr><tr><td>bar</td></tr>');
请注意,使用不同容器元素(例如容器)的div
类似方法不太可行。HTML对允许哪些元素类型存在于其他元素类型中有限制;例如,您不能将a td
作为的直接子对象div
。这将导致这些元素,如果你尝试设置消失innerHTML
的div
遏制他们。以来<template>
s对它们的内容没有任何限制,因此使用模板时此缺点不适用。
但是,template
某些旧版浏览器不支持。截至2018年1月,我可以使用...估计全球90%的用户正在使用支持template
s 的浏览器。特别是,没有版本的Internet Explorer支持它们。template
直到Edge发行,Microsoft才实现支持。
如果您很幸运地编写了仅针对现代浏览器用户的代码,请立即使用它们。否则,您可能需要等待一段时间才能赶上用户。
html.trim
内部HTML设置的内部解析而是通过内部解析来删除的空格。就我而言,它删除了textNode中重要的空间。:-(
使用insertAdjacentHTML()。它适用于当前所有浏览器,甚至IE11。
var mylist = document.getElementById('mylist');
mylist.insertAdjacentHTML('beforeend', '<li>third</li>');
<ul id="mylist">
<li>first</li>
<li>second</li>
</ul>
insertAdjacentHTML
适用于IE 4.0以后的所有浏览器。
innerHTML += ...
是使用此方法对以前元素的引用仍然完整。
beforebegin
,afterbegin
,beforeend
,afterend
。请参阅MDN文章。
较新的DOM实现具有 range.createContextualFragment
,它以与框架无关的方式了您想要的功能。
它得到了广泛的支持。但可以肯定的是,请在同一MDN链接中向下检查其兼容性,因为它会不断变化。截至2017年5月,情况如下:
Feature Chrome Edge Firefox(Gecko) Internet Explorer Opera Safari
Basic support (Yes) (Yes) (Yes) 11 15.0 9.1.2
innerHTML
div的缺点类似。某些元素(如td
)将被忽略,并且不会出现在结果片段中。
无需任何调整,您就获得了本机API:
const toNodes = html =>
new DOMParser().parseFromString(html, 'text/html').body.childNodes[0]
<td>text</td>
。这是因为DOMParser
试图解析完整的HTML文档,并且并非所有元素都有效作为文档的根元素。
对于某些HTML片段,例如<td>test</td>
div.innerHTML,DOMParser.parseFromString和range.createContextualFragment(没有正确的上下文),在本文其他答案中提到的解决方案将不会创建该<td>
元素。
jQuery.parseHTML()正确地处理了它们(我将jQuery 2的parseHTML函数提取为一个可在非jquery代码库中使用的独立函数)。
如果仅支持Edge 13+,则仅使用HTML5模板标记会更简单:
function parseHTML(html) {
var t = document.createElement('template');
t.innerHTML = html;
return t.content.cloneNode(true);
}
var documentFragment = parseHTML('<td>Test</td>');
这是一种简单的方法:
String.prototype.toDOM=function(){
var d=document
,i
,a=d.createElement("div")
,b=d.createDocumentFragment();
a.innerHTML=this;
while(i=a.firstChild)b.appendChild(i);
return b;
};
var foo="<img src='//placekitten.com/100/100'>foo<i>bar</i>".toDOM();
document.body.appendChild(foo);
您可以使用以下命令从字符串创建有效的DOM节点:
document.createRange().createContextualFragment()
下面的示例在页面中添加一个按钮元素,该元素从字符串中获取标记:
let html = '<button type="button">Click Me!</button>';
let fragmentFromString = function (strHTML) {
return document.createRange().createContextualFragment(strHTML);
}
let fragment = fragmentFromString(html);
document.body.appendChild(fragment);
DocumentFragment
对象,但令我惊讶的是,它仍然遭受与公认答案相同的缺陷:如果这样做document.createRange().createContextualFragment('<td>bla</td>')
,您将得到一个仅包含文本'bla'而不包含<td>
元素的片段。至少,这就是我在Chrome 63中观察到的;我没有深入研究规范来确定它是否正确。
使用原型,您还可以执行以下操作:
HTML:
<ul id="mylist"></ul>
JS:
$('mylist').insert('<li>text</li>');
为了进一步增强我们可以在不同位置找到的有用的.toDOM()代码段,我们现在可以安全地使用反引号(模板文字)。
因此,我们可以在foo html声明中使用单引号和双引号。
对于熟悉该术语的人来说,其行为类似于heredocs。
可以使用变量进一步增强它,以进行复杂的模板制作:
模板文字用反
引号()(重音符)而不是双引号或单引号引起来。模板文字可以包含占位符。这些由美元符号和大括号($ {expression})表示。占位符中的表达式及其之间的文本将传递给函数。默认功能只是将各个部分串联为一个字符串。如果在模板文字之前有一个表达式(在此处标记),则称为“标记模板”。在这种情况下,将使用已处理的模板文字来调用标签表达式(通常是一个函数),然后可以在输出之前对其进行操作。要在模板文字中避免反引号,请将反斜线\放在反引号之前。
String.prototype.toDOM=function(){
var d=document,i
,a=d.createElement("div")
,b=d.createDocumentFragment()
a.innerHTML = this
while(i=a.firstChild)b.appendChild(i)
return b
}
// Using template litterals
var a = 10, b = 5
var foo=`
<img
onclick="alert('The future start today!')"
src='//placekitten.com/100/100'>
foo${a + b}
<i>bar</i>
<hr>`.toDOM();
document.body.appendChild(foo);
img {cursor: crosshair}
那么,为什么不直接使用.innerHTML +=
呢?这样,浏览器将重新计算整个DOM,速度要慢得多。
我添加了一个Document
原型,该原型从字符串创建一个元素:
Document.prototype.createElementFromString = function (str) {
const element = new DOMParser().parseFromString(str, 'text/html');
const child = element.documentElement.querySelector('body').firstChild;
return child;
};
<template>
演示版
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @description HTML5 Template
* @augments
* @example
*
*/
/*
<template>
<h2>Flower</h2>
<img src="https://www.w3schools.com/tags/img_white_flower.jpg">
</template>
<template>
<div class="myClass">I like: </div>
</template>
*/
const showContent = () => {
// let temp = document.getElementsByTagName("template")[0],
let temp = document.querySelector(`[data-tempalte="tempalte-img"]`),
clone = temp.content.cloneNode(true);
document.body.appendChild(clone);
};
const templateGenerator = (datas = [], debug = false) => {
let result = ``;
// let temp = document.getElementsByTagName("template")[1],
let temp = document.querySelector(`[data-tempalte="tempalte-links"]`),
item = temp.content.querySelector("div");
for (let i = 0; i < datas.length; i++) {
let a = document.importNode(item, true);
a.textContent += datas[i];
document.body.appendChild(a);
}
return result;
};
const arr = ["Audi", "BMW", "Ford", "Honda", "Jaguar", "Nissan"];
if (document.createElement("template").content) {
console.log("YES! The browser supports the template element");
templateGenerator(arr);
setTimeout(() => {
showContent();
}, 0);
} else {
console.error("No! The browser does not support the template element");
}
@charset "UTf-8";
/* test.css */
:root {
--cololr: #000;
--default-cololr: #fff;
--new-cololr: #0f0;
}
[data-class="links"] {
color: white;
background-color: DodgerBlue;
padding: 20px;
text-align: center;
margin: 10px;
}
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Template Test</title>
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<section>
<h1>Template Test</h1>
</section>
<template data-tempalte="tempalte-img">
<h3>Flower Image</h3>
<img src="https://www.w3schools.com/tags/img_white_flower.jpg">
</template>
<template data-tempalte="tempalte-links">
<h3>links</h3>
<div data-class="links">I like: </div>
</template>
<!-- js -->
</body>
</html>
迟了,但只是作为笔记;
可以向目标元素添加一个琐碎的元素作为容器,并在使用后将其删除。
//在chrome 23.0,firefox 18.0(即7-8-9和Opera 12.11)上进行了测试。
<div id="div"></div>
<script>
window.onload = function() {
var foo, targetElement = document.getElementById('div')
foo = document.createElement('foo')
foo.innerHTML = '<a href="#" target="_self">Text of A 1.</a> '+
'<a href="#" onclick="return !!alert(this.innerHTML)">Text of <b>A 2</b>.</a> '+
'<hr size="1" />'
// Append 'foo' element to target element
targetElement.appendChild(foo)
// Add event
foo.firstChild.onclick = function() { return !!alert(this.target) }
while (foo.firstChild) {
// Also removes child nodes from 'foo'
targetElement.insertBefore(foo.firstChild, foo)
}
// Remove 'foo' element from target element
targetElement.removeChild(foo)
}
</script>
从字符串呈现DOM的最快解决方案:
let render = (relEl, tpl, parse = true) => {
if (!relEl) return;
const range = document.createRange();
range.selectNode(relEl);
const child = range.createContextualFragment(tpl);
return parse ? relEl.appendChild(child) : {relEl, el};
};
而这里 ü可以检查DOM操作的性能阵营VS本土JS
现在您可以简单地使用:
let element = render(document.body, `
<div style="font-size:120%;line-height:140%">
<p class="bold">New DOM</p>
</div>
`);
当然,在不久的将来,您会使用内存中的引用,因为var“ element”是您在文档中新创建的DOM。
并记住“ innerHTML =“非常慢:/
这是我的代码,它可以正常工作:
function parseTableHtml(s) { // s is string
var div = document.createElement('table');
div.innerHTML = s;
var tr = div.getElementsByTagName('tr');
// ...
}
对于这个问题,我想我会分享我提出的复杂但简单的方法……也许有人会发现有用的东西。
/*Creates a new element - By Jamin Szczesny*/
function _new(args){
ele = document.createElement(args.node);
delete args.node;
for(x in args){
if(typeof ele[x]==='string'){
ele[x] = args[x];
}else{
ele.setAttribute(x, args[x]);
}
}
return ele;
}
/*You would 'simply' use it like this*/
$('body')[0].appendChild(_new({
node:'div',
id:'my-div',
style:'position:absolute; left:100px; top:100px;'+
'width:100px; height:100px; border:2px solid red;'+
'cursor:pointer; background-color:HoneyDew',
innerHTML:'My newly created div element!',
value:'for example only',
onclick:"alert('yay')"
}));
为什么不使用本机js?
var s="<span class='text-muted' style='font-size:.75em; position:absolute; bottom:3px; left:30px'>From <strong>Dan's Tools</strong></span>"
var e=document.createElement('div')
var r=document.createRange();
r.selectNodeContents(e)
var f=range.createContextualFragment(s);
e.appendChild(f);
e = e.firstElementChild;
您可以使用以下函数将文本“ HTML”转换为元素
function htmlToElement(html)
{
var element = document.createElement('div');
element.innerHTML = html;
return(element);
}
var html="<li>text and html</li>";
var e=htmlToElement(html);
td
。
这也将起作用:
$('<li>').text('hello').appendTo('#mylist');
感觉更像是带有链接函数调用的jquery方式。