【问题标题】:infinite-loop via prepend element in DOM通过在 DOM 中添加元素的无限循环
【发布时间】:2013-03-28 00:40:37
【问题描述】:

不寻找使用框架 XXX 答案

此问题并非旨在通过框架找到实用的解决方案。用使用framework XXX回答,或者这在framework XXX里这么简单,或者为什么不用这个framework XXX???不回答问题。

我有一个要在页面加载后运行的函数:performShim。此函数遍历 DOM 中所有带有 span 标签的元素,检查它们是否有 classNameshim,如果有,则调用 shim,将匹配元素的引用传递给它。

我的目标是添加另一个包含iframespan 到传递给shim 的元素。

使用到目前为止我编写的代码,我能够追加 到元素的父级就好了。但是,如果我注释掉 append 行并尝试使用 prepend 行,浏览器可能会陷入无限循环。

这对我来说并不明显。

function shim( element ) {      
    var iframe = document.createElement('iframe');
    iframe.setAttribute( 'frameborder', '0' );
    iframe.setAttribute( 'scrolling', 'no' );
    iframe.setAttribute( 'align', 'bottom' );
    iframe.setAttribute( 'marginheight', '0' );
    iframe.setAttribute( 'marginwidth', '0' );
    iframe.setAttribute( 'src', "javascript:'';" ); 

    var span = document.createElement('span');
    span.appendChild(iframe);


    //element.parentNode.insertBefore(span,element); //causes infinite loop?

    element.parentNode.appendChild(span); //this line works OK

    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'inline';
    var width = element.offsetWidth;
    var height = element.offsetHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;

    iframe.style.width = (width-6) + 'px';
    iframe.style.height = (height-6) + 'px';

}   

function performShim() {
    var children = document.getElementsByTagName("span");   
    for( var i = 0; i < children.length; i++ ) {
        if( children[i].className == "shim" ) {
            shim(children[i]);  
        }
    }
} 

【问题讨论】:

  • This question is not intended for finding a practical solution 那就发错地方了。
  • @LightnessRacesinOrbit:你通过框架忽略了[...],这是一个完全有效的需求。
  • 对不起,这个问题是为了更好地理解 DOM 和 DOM 相关的纯 JavaScript 函数,而不是更好地理解框架如何让你的生活更轻松。我对给出的答案非常满意。
  • @Felix:你忽略了我史诗般的幽默
  • @LightnessRacesinOrbit:也许我的幽默检测被看似与您的评论相关的微小接近投票蒙蔽了。道歉,没有难过的感觉;)

标签: javascript dom internet-explorer-6 infinite-loop


【解决方案1】:

NodeList(例如document.getElementsByTagName 返回的那个)通常是一个实时列表——您对DOM 所做的更改也会显示在其中。因此,每次您在当前元素之前添加 span 时,都会将列表扩展一个元素并将当前元素移动一个元素,下一次迭代会将您返回到刚刚完成的节点。

您有几个简单的解决方法...

  • 当你添加一个节点时撞倒计数器。(丑陋,如果你最终添加了一些东西而不是span,你最终会跳过节点并且它赢了'不明白为什么。)

  • 将列表复制到数组并遍历数组。您可以使用类似
    children = [].slice.call(children, 0);(更常见)或
    children = Array.apply(window, children);

  • 使用document.querySelectorAll,它会返回一个不活跃的NodeList。 (即使它是实时的,在这种情况下,您也可以选择 'span.shim',插入的 span 无论如何都不会显示在其中。)

  • 向后迭代(从 children.length - 1 到 0)。

【讨论】:

  • +1。 下一次迭代会让你回到刚刚完成的节点这是重要的部分。追加是“好的”(在这种情况下),因为在下一次迭代中,您将处理另一个节点,最终您将用完 spanshim 的节点。但是如果生成的spans 也被分配了shim 类,它也会生成一个无限循环。
  • 所以performShim 中的变量children 只是对实时列表的引用吗?如果是这种情况,为什么附加 span 可以正常工作,但前置却不行?
  • @user1169578:请参阅我的评论。关于前置,考虑使用节点a, b。你的循环变量是i = 0。现在您正在处理a 和前置节点c。结果是c, a, b。您的循环变量前进到i = 1,这又是节点a...等等。如果您追加,您的列表可能看起来像a, b, c,一旦您处理c,您将不再追加任何节点,因为c 没有类shim
  • 如果您使用.querySelectorAll(),您可以按类选择,因此(在这种情况下)返回是否实时并不重要。对于选择所有跨度的现有代码,您可以向后迭代循环。
  • 谢谢,我想通过getElementsByTagName 等函数检索到的集合是一个live collection,这是一个微妙的点。我选择将初始集合复制到一个数组并遍历该数组。感谢大家解释这个微妙但非常重要的区别。 @Felix King:你的描述很敏锐,我现在什么都明白了。
猜你喜欢
  • 2018-11-03
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多