使用 JavaScript 复制元素(及其样式)
Posted
技术标签:
【中文标题】使用 JavaScript 复制元素(及其样式)【英文标题】:Duplicating an element (and its style) with JavaScript 【发布时间】:2010-12-23 08:04:37 【问题描述】:对于我正在实现的 javascript 库,我需要 克隆一个与原始应用样式完全相同的元素。虽然我已经获得了相当不错的 JavaScript 知识,但作为一种编程语言,在开发它时,我仍然是一个 DOM 脚本新手,所以任何关于如何实现这一点的建议都会非常有帮助(而且必须完成不使用任何其他 JavaScript 库)。
非常感谢您。
编辑:cloneNode(true)
不会克隆元素的计算样式。假设您有以下 html:
<body>
<p id="origin">This is the first paragraph.</p>
<div id="destination">
<p>The cloned paragraph is below:</p>
</div>
</body>
还有一些风格,比如:
body > p
font-size: 1.4em;
font-family: Georgia;
padding: 2em;
background: rgb(165, 177, 33);
color: rgb(66, 52, 49);
如果你只是克隆元素,使用类似的东西:
var element = document.getElementById('origin');
var copy = element.cloneNode(true);
var destination = document.getElementById('destination');
destination.appendChild(copy);
样式不会被克隆。
【问题讨论】:
【参考方案1】:您不仅需要克隆,而且可能还需要进行深度克隆。
node.cloneNode(true);
文档是here。
如果 deep 设置为 false,则没有 子节点被克隆。任何文字 包含的节点未克隆 要么,因为它包含在一个或 更多子文本节点。
如果 deep 评估为真,则整个 子树(包括可能在 子文本节点)也被复制。为了 空节点(例如 IMG 和 INPUT 元素)是否无关紧要 deep 设置为 true 或 false 但您 仍然必须提供一个值。
编辑:OP 声明 node.cloneNode(true)
没有复制样式。下面是一个简单的测试,它使用 jQuery 和标准 DOM API 显示了相反的结果(以及预期的效果):
var node = $("#d1");
// Add some arbitrary styles
node.css("height", "100px");
node.css("border", "1px solid red");
// jQuery clone
$("body").append(node.clone(true));
// Standard DOM clone (use node[0] to get to actual DOM node)
$("body").append(node[0].cloneNode(true));
结果可见:http://jsbin.com/egice3/
编辑 2
希望你之前提到过;) 计算风格是完全不同的。更改您的 CSS 选择器或将该样式应用为一个类,您将获得解决方案。
编辑 3
因为这个问题是一个合法的问题,我没有找到任何好的解决方案,它让我很困扰,想出了以下问题。它不是特别优雅,但它可以完成工作(仅在 FF 3.5 中测试过)。
var realStyle = function(_elem, _style)
var computedStyle;
if ( typeof _elem.currentStyle != 'undefined' )
computedStyle = _elem.currentStyle;
else
computedStyle = document.defaultView.getComputedStyle(_elem, null);
return _style ? computedStyle[_style] : computedStyle;
;
var copyComputedStyle = function(src, dest)
var s = realStyle(src);
for ( var i in s )
// Do not use `hasOwnProperty`, nothing will get copied
if ( typeof s[i] == "string" && s[i] && i != "cssText" && !/\d/.test(i) )
// The try is for setter only properties
try
dest.style[i] = s[i];
// `fontSize` comes before `font` If `font` is empty, `fontSize` gets
// overwritten. So make sure to reset this property. (hackyhackhack)
// Other properties may need similar treatment
if ( i == "font" )
dest.style.fontSize = s.fontSize;
catch (e)
;
var element = document.getElementById('origin');
var copy = element.cloneNode(true);
var destination = document.getElementById('destination');
destination.appendChild(copy);
copyComputedStyle(element, copy);
有关更多信息和一些注意事项,请参阅 PPK 题为 Get Styles 的文章。
【讨论】:
感谢 Justing(和 S. Mark)的回复,但不幸的是,cloneNode(我使用它来克隆元素)对样式没有任何作用。这是我的问题的一部分:如何不仅克隆元素,还克隆应用于它的样式。 我可以发誓事实并非如此。让我做一两个测试。 不,恐怕不是,贾斯汀。 ;) 非常感谢您为完成上述示例所做的努力,但在我的测试中,样式没有被克隆(我已经编辑了我的原始问题以尝试显示它)。可能是因为您使用的是 jQuery 吗?或者因为 cloneNode 可能是克隆样式直接应用于 JavaScript 中的元素,而不是计算的元素? (我只是猜测。) 这不是因为使用了 jQuery。我同时使用了 jQuery 和标准 DOM 方法。 我明白了。所以这就是我对计算样式的想法,不是吗?关于您的第二次编辑——顺便说一句,我很抱歉在我的第一个问题中没有更具体;)——恐怕它不像你提出的解决方案那么容易,贾斯汀。当然,可以针对具体情况这样做,例如我的示例,但是我一直在寻找一种适用于每种情况的通用解决方案,无论样式是如何分配给原始元素的(如果它们是继承的,设置多个规则,使用不同的选择器等)。【参考方案2】:这些都不适合我,但我是根据 Luigi 的回答想出这个的。
copyStyles(source: HTMLElement, destination: HTMLElement)
// Get a list of all the source and destination elements
const srcElements = <HTMLCollectionOf<HTMLElement>>source.getElementsByTagName('*');
const dstElements = <HTMLCollectionOf<HTMLElement>>destination.getElementsByTagName('*');
// For each element
for (let i = srcElements.length; i--;)
const srcElement = srcElements[i];
const dstElement = dstElements[i];
const sourceElementStyles = document.defaultView.getComputedStyle(srcElement, '');
const styleAttributeKeyNumbers = Object.keys(sourceElementStyles);
// Copy the attribute
for (let j = 0; j < styleAttributeKeyNumbers.length; j++)
const attributeKeyNumber = styleAttributeKeyNumbers[j];
const attributeKey: string = sourceElementStyles[attributeKeyNumber];
dstElement.style[attributeKey] = sourceElementStyles[attributeKey];
【讨论】:
【参考方案3】:在查看了整个 WEB 上的几个很好的解决方案后,我决定将每个解决方案的所有最佳方面结合起来并提出这个。
我将我的解决方案留在了简单的超快速 Javascript 中,这样每个人都可以翻译成他们本月最新的、很棒的 JS 风格。
代表来自马尼拉的香草.....
* @problem: Sometimes .cloneNode(true) doesn't copy the styles and your are left
* with everything copied but no styling applied to the clonedNode (it looks plain / ugly). Solution:
*
* @solution: call synchronizeCssStyles to copy styles from source (src) element to
* destination (dest) element.
*
* @author: Luigi D'Amico (www.8bitplatoon.com)
*
*/
function synchronizeCssStyles(src, destination, recursively)
// if recursively = true, then we assume the src dom structure and destination dom structure are identical (ie: cloneNode was used)
// window.getComputedStyle vs document.defaultView.getComputedStyle
// @TBD: also check for compatibility on IE/Edge
destination.style.cssText = document.defaultView.getComputedStyle(src, "").cssText;
if (recursively)
var vSrcElements = src.getElementsByTagName("*");
var vDstElements = destination.getElementsByTagName("*");
for (var i = vSrcElements.length; i--;)
var vSrcElement = vSrcElements[i];
var vDstElement = vDstElements[i];
// console.log(i + " >> " + vSrcElement + " :: " + vDstElement);
vDstElement.style.cssText = document.defaultView.getComputedStyle(vSrcElement, "").cssText;
【讨论】:
神圣的大规模标记蝙蝠侠,这最终得到了 crazy 大 HTML。不过非常非常快,我+1 感谢 Dan - 正确,它确实会导致 HTML (CSS) 变大 - 它确实变成了一个怪物 - 它采用所有默认和应用的样式并复制它们。以上是关于使用 JavaScript 复制元素(及其样式)的主要内容,如果未能解决你的问题,请参考以下文章
Javascript setInterval();不工作? [复制]