在DOM之外使用jQuery克隆对象

8
我一直在做一个小项目,其中使用了jQuery的.clone()方法。这种方法的陷阱是在具有唯一标识符的HTML上使用它。因此,我开始实现getComputedStyle来查找原始唯一元素的样式属性,以便将其复制到克隆体中,并在此之后给其一个新的id(是的,这可能会导致性能问题,但这只是试验性的)。
根据jQuery规范,在克隆后但附加前执行此操作将使操作发生在DOM之外(因此不会发生id“违规”)。但是,当我尝试在对象被克隆后查找元素的样式属性时,我注意到不同浏览器之间存在一些奇怪的行为。在克隆之前,所有浏览器都返回相同的值,但在克隆之后:
  • Firefox - 无忧无虑,有趣的是克隆体的计算样式是实际的CSS值,而不是计算数据(以像素为单位)。

  • IE - 似乎可以工作,但值不一定正确。

  • Chrome - 不计算。以下是一个示例:

http://codepen.io/anon/pen/zxqmNK?editors=011

var elements = [];
var objects = [];

$('body').find('[id]').each(function() {
    elements.push(this);
});

$('body').clone().find('[id]').each(function() {
    objects.push(this);
});

$.each(elements, function(key, element) {
    var current = window.getComputedStyle(element, null).getPropertyValue('width');
    $('#log').append('<p>' + element.id + ': ' + current + '</p>');
});

$('#log').append('</br>');

$.each(objects, function(count, object) {
    var current = window.getComputedStyle(object, null).getPropertyValue('width');
    $('#log').append('<p>' + object.id + ': ' + current + '</p>');
});

有人知道这是一个bug还是类似的行为之前就已经出现过吗? 网上没有太多信息(甚至没有Stackoverflow)。 感谢您提前的任何见解。

编辑 - 进行了更多测试,看起来IE的行为与Chrome相同。 只是不返回任何东西,一切都设置为“自动”。 如果使用.css()访问克隆对象的样式,则所有值都返回0px(包括背景等属性)。 似乎只有Mozilla将克隆对象视为已应用任何样式。


2
不是直接回答,但你为什么要这么多使用 getComputedStyle 呢?使用类来设置元素样式难道不更容易和可预测吗? - Matijs
谢谢,anpsmn。我简直不敢相信我没有找到那个。我确实决定按照Felix Kling的建议去做(从原始对象复制样式)。虽然如果它可以跨浏览器使用,Mozilla的处理方式会非常棒。Matijs,你当然是对的,但这段代码是作为一个小插件(放大镜)提取出来的,我不想强迫人们改变他们的文件结构。当然我会推荐使用类。感谢回复。 - Shikkediel
1个回答

1

第一种方法

这是我最初的解决方案...虽然诱人,但将Mozilla单独处理需要浏览器嗅探,因此我们将绕过无法访问克隆样式的问题。

创建两个对象数组,包含唯一标识符的元素 - 第一个将包含要从中复制样式的元素,第二个将保存要传输样式的克隆元素:

var individual = [], singular = [];

$('.target').find('[id]').each(function() {

    individual.push(this);
})
.end().clone().find('[id]').each(function() {

    singular.push(this);
});

现在,属性及其值从存储在DOM内的对象数组中复制到克隆体中,然后将当前标识符的名称更改为唯一的名称。
$.each(individual, function(key) {

    var tag = this.id,
    styles = window.getComputedStyle(this, null),
    element = singular[key];

    $.each(styles, function() {

        var value = styles.getPropertyValue(this);
        $(element).css(this, value);
    });

    $(element).attr('id', tag + '-cloned');
});

克隆的项将被插入到此后面,因此不会出现重复标识符。请注意,这可能会产生大量样式属性(例如在Firefox中每个对象约为220个)。

演示

var individual = [], singular = [];

$('.target').find('[id]').each(function() {

    individual.push(this);
})
.end().clone().find('[id]').each(function() {

    singular.push(this);
})
.end().queue(function() {

    transFigure();
    $(this).dequeue();
})
.appendTo('body');

function transFigure() {

$.each(individual, function(key) {

    var tag = this.id,
    styles = window.getComputedStyle(this, null),
    element = singular[key];

    $.each(styles, function() {

        var value = styles.getPropertyValue(this);
        $(element).css(this, value);
    });

    $(element).attr('id', tag + '-cloned');
});
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

第二种方法

虽然上述方法可以正常工作,但效率不高,在页面调整大小时,值可能会开始发生变化。因此,在JavaScript的cssRules中进行挖掘后,我找到了一种更好的解决方案。使用这个方法,您可以直接访问所有样式表!

下面是一个示例,试图解释这个过程,但实际上是通过将克隆体中的唯一标识符(使用.test)与样式表中找到的cssText进行匹配。然后更改id并将其存储在数组中,以便稍后插入/添加到样式表本身。

示例

除了更有效的方法(不传输所有默认值)之外,实际的CSS也被复制到所有浏览器中,而不是计算出来的值。任何派生类,例如imgp等,也可以包括在内。它甚至复制了@rules,保持响应性完好无损。

它的本质:

var singular = [], rules = [];

$('#target').clone().find('[id]').each(function() {

    singular.push(this);
});

var sheet = document.styleSheets[0],
styles = sheet.cssRules;

$.each(singular, function() {

    var selector = '#' + this.id,
    pattern = new RegExp(selector + '(:| |,)');

    $.each(styles, function() {

        var string = this.cssText;

        if (pattern.test(string)) {
        var rule = string.replace(selector, selector + '-duplicate');
        rules.push(rule);
        }
    });
});

$.each(rules, function() {

    var index = styles.length;
    sheet.insertRule(this, index);
});

在此之后,克隆体可以插入到DOM中,并应用所有唯一标识符和完整样式。请注意,在上面的示例中,为了使代码在使用cssRules时尽可能易读,实际上并没有执行此操作。图像事先已经放置在标记中,具有不同的id——与复制的样式规则匹配。

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