Šim*_*das 46 html javascript browser jquery
假设我们x
在页面上有一个DIV ,我们想要将该DIV的内容复制("复制粘贴")到另一个DIV中y
.我们可以这样做:
y.innerHTML = x.innerHTML;
Run Code Online (Sandbox Code Playgroud)
或者使用jQuery:
$(y).html( $(x).html() );
Run Code Online (Sandbox Code Playgroud)
但是,看起来这种方法不是一个好主意,应该避免.
(1)为什么要避免这种方法?
(2)应如何做呢?
更新:
为了这个问题,让我们假设DIV中没有ID的元素x
.
(对不起,我忘了在原来的问题中报道这个案子.)
结论:
我已经在下面发布了我自己的答案(正如我原先的意图).现在,我也计划接受我自己的答案:P
,但是我的答案是如此惊人,以至于我不得不接受它.
lon*_*day 76
这种将HTML元素从一个地方"复制"到另一个地方的方法是误解浏览器所做的事情的结果.浏览器不会在某处将HTML文档保留在内存中,而是根据JavaScript中的命令反复修改HTML.
当浏览器首次加载页面时,它会解析 HTML文档并将其转换为DOM结构.这是遵循W3C标准的对象关系(好吧,主要是......).从那时起,原始HTML完全冗余.浏览器不关心原始HTML结构是什么; 它对网页的理解是从它创建的DOM结构.如果您的HTML标记不正确/无效,将通过Web浏览器以某种方式进行更正; DOM结构不会以任何方式包含无效代码.
基本上,HTML应被视为一种序列化DOM结构的方式,该结构将通过互联网传递或存储在本地文件中.
因此,它不应该用于修改现有的网页.DOM(文档对象模型)具有用于改变页面内容的系统.这基于节点的关系,而不是HTML序列化.所以当你添加一个li
a时ul
,你有这两个选项(假设ul
是list元素):
// option 1: innerHTML
ul.innerHTML += '<li>foobar</li>';
// option 2: DOM manipulation
var li = document.createElement('li');
li.appendChild(document.createTextNode('foobar'));
ul.appendChild(li);
Run Code Online (Sandbox Code Playgroud)
现在,第一个选项看起来更简单了,但这只是因为浏览器为你抽象了很多:在内部,浏览器必须将元素的子元素转换为字符串,然后附加一些内容,然后将字符串转换回DOM结构.第二个选项对应于浏览器对正在发生的事情的本地理解.
第二个主要考虑因素是考虑HTML的局限性.当您考虑网页时,并非与该元素相关的所有内容都可以序列化为HTML.例如,绑定x.onclick = function();
或x.addEventListener(...)
不会复制的事件处理程序innerHTML
,因此不会复制它们.所以新元素y
将不会有事件监听器.这可能不是你想要的.
所以解决这个问题的方法是使用原生DOM方法:
for (var i = 0; i < x.childNodes.length; i++) {
y.appendChild(x.childNodes[i].cloneNode(true));
}
Run Code Online (Sandbox Code Playgroud)
阅读MDN文档可能有助于理解这种做法:
现在这个问题(与上面的代码示例中的选项2一样)是它非常详细,比innerHTML
选项要长得多.这是当你喜欢有一个JavaScript库为你做这种事情.例如,在jQuery中:
$('#y').html($('#x').clone(true, true).contents());
Run Code Online (Sandbox Code Playgroud)
关于你想要发生什么,这更明确.例如,除了具有各种性能优势和保留事件处理程序之外,它还可以帮助您了解代码的作用.作为一个JavaScript程序员,这对你的灵魂有好处,并且使得奇怪的错误显着降低!
首先,让我们定义必须在此完成的任务:
DIV的所有子节点x
必须被"复制"(连同其所有后代=深拷贝)并"粘贴"到DIV中y
.如果任何后代x
有一个或多个事件处理程序绑定到它,我们可能会希望这些处理程序继续处理副本(一旦它们被放入其中y
).
现在,这不是一项微不足道的任务.幸运的是,jQuery库(以及我认为的所有其他流行的库)提供了一种方便的方法来完成这项任务:.clone()
.使用此方法,解决方案可以这样编写:
$( x ).contents().clone( true ).appendTo( y );
Run Code Online (Sandbox Code Playgroud)
上述解决方案是问题(2)的答案.现在,我们来解决问题(1):
这个
y.innerHTML = x.innerHTML;
Run Code Online (Sandbox Code Playgroud)
这不仅仅是一个坏主意 - 它是一个糟糕的主意.让我解释...
上述陈述可分为两个步骤.
表达式x.innerHTML
被评估,
分配该表达式的返回值(这是一个字符串)y.innerHTML
.
我们要复制的节点(子节点x
)是DOM节点.它们是浏览器内存中存在的对象.在评估时x.innerHTML
,浏览器将这些DOM节点序列化(字符串化)为字符串(HTML源代码字符串).
现在,如果我们需要这样的字符串(例如,将其存储在数据库中),那么这种序列化将是可以理解的.但是,我们不需要这样的字符串(至少不是最终产品).
在第2步中,我们将此字符串分配给y.innerHTML
.浏览器通过解析导致一组DOM节点的字符串来对此进行评估,然后将这些节点插入到DIV中y
(作为子节点).
所以,总结一下:
子节点x
- > 字符串化 - > HTML源代码字符串 - > 解析 - >节点(副本)
那么,这种方法有什么问题呢?那么,DOM节点可能包含不能并因此不会被序列化的属性和功能.最重要的此类功能是绑定到后代的事件处理程序x
- 这些元素的副本不会绑定任何事件处理程序.处理程序在此过程中迷失了方向.
这里可以做一个有趣的比喻:
数字信号 - > D/A转换 - >模拟信号 - > A/D转换 - >数字信号
您可能知道,由此产生的数字信号并不是原始数字信号的精确副本 - 某些信息在此过程中丢失了.
我希望你现在明白为什么y.innerHTML = x.innerHTML
要避免.