【问题标题】:Duplicating an element (and its style) with JavaScript使用 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);

样式不会被克隆。

【问题讨论】:

    标签: javascript css dom


    【解决方案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) 变大 - 它确实变成了一个怪物 - 它采用所有默认和应用的样式并复制它们。
      猜你喜欢
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-02
      相关资源
      最近更新 更多