快速与容器内的大量元素进行交互(DOM,javascript)

时间:2014-03-05 10:58:31

标签: javascript html dom

所以我在容器div中有大量的div(4000-5000)[每个包含跨度,锚点,图像等],基本上我将它们的显示设置为none或基于条件阻止。这确实需要一些时间。

在我搜索更快的内容时,我遇到了这个页面https://developers.google.com/speed/articles/javascript-dom,解决方法是从DOM中删除容器div,并通过getElementsByTagName迭代包含的元素。

/**
 * Remove an element and provide a function that inserts it into its original position
 * @param element {Element} The element to be temporarily removed
 * @return {Function} A function that inserts the element into its original position
 **/
function removeToInsertLater(element) {
  var parentNode = element.parentNode;
  var nextSibling = element.nextSibling;
  parentNode.removeChild(element);
  return function() {
    if (nextSibling) {
      parentNode.insertBefore(element, nextSibling);
    } else {
      parentNode.appendChild(element);
    }
  };
}


function updateAllAnchors(element, anchorClass) {
  var insertFunction = removeToInsertLater(element);
  var anchors = element.getElementsByTagName('a');
  for (var i = 0, length = anchors.length; i < length; i ++) {
    anchors[i].className = anchorClass;
  }
  insertFunction();
}

问题是我无法使用提供的解决方案,因为我需要通过其ID访问子元素,我不能这样做,因为元素已从DOM中删除。有没有办法实现这个目标?

我还尝试删除容器div并将其附加到文档片段,但是当它们位于documentfragment中时,仍然无法通过ID访问5000个元素

最后,我也尝试了这个:

document.getElementById("CONTAINERDIV").style.display = "none";

//iterate through the 5000 children divs and change their classname

document.getElementById("CONTAINERDIV").style.display = "block";

因为我希望它不会在每次迭代时触发重排,但这似乎并没有提供所需时间的改进。

有没有人对此有任何想法?

6 个答案:

答案 0 :(得分:8)

我会尝试按要求提供来源。

第一个解决方案 - 最佳解决方案
根据这个网站:JavaScript Grid with One Million Records
你可以学到几件重要的事情:

  1. 大量DOM节点使渲染变慢
  2. JavaScript数组可以处理大型数据集
  3. 通过大型数组循环很快
  4. 通过向Array.sort()提供自定义函数对数组进行排序很快
  5. eval()很慢,不应该在大循环中使用
  6. 所以,我建议你构建一个数组来快速处理你的元素。

    第二种解决方案
    从本网站获取的另一个解决方案:Processing large amounts of data in JavaScript
    将使用超时(听起来很奇怪)来提高处理程序的速度。
    这个想法来自Book: Secrets of the JavaScript Ninja

答案 1 :(得分:5)

如果您只想显示/隐藏,而不想更改div的DOM中的任何内容,并且您知道所有ID,我认为,将此归档的最佳(最快)方法是准备<style />元素和将它附加到DOM。样式el应包含所有ID和正确显示。迭代ID并将其添加到CSS字符串,然后创建<style />元素并向其追加字符串。这应该适合你。

答案 2 :(得分:4)

事先构建一个id-to-element map / hash表:

var map = {};

for (var i = 0, l = ids.length; i < l; i++) {
    map[ids[i]] = document.getElementById(ids[i]);
}

其中ids是元素ID列表。 (如果你需要5000个元素的ID,我假设你有一个列表或者可以生成一个。)

然后,当您从DOM中删除容器元素时,您可以使用地图按ID查找元素。

答案 3 :(得分:3)

以下两个陈述是等效的(第二种方法需要#,但可用于所有元素,而不仅仅是文档。)

elt = document.getElementById("theId");
elt = document.querySelector("#theId");

因此,您可以使用element.querySelector('#theId')根据其ID访问子元素(即使父element当前与DOM分离)。

请注意querySelector() is not supported in IE 7及更早版本。

答案 4 :(得分:3)

显示无/块很贵。从我提高交易网络平台性能的那些日子开始,我推荐的一种特别适用于旧版浏览器的技术是使用相对位置并使用负左值将其从屏幕上拉出来。当然,根据您的实现,您可能还希望将高度设置为0px或查看位置绝对的可能性。核心概念仍然是你只是将元素从屏幕上移开。好消息是隐藏的元素仍然存在于DOM中,您可以访问它们。

div {
  position: relative;
  left: 0px;
}
div.hide {
  left: -4096px;
  height: 0px;
} 

查看这两个小提琴,他们创建10K行并切换(隐藏/显示)奇数行:

FIDDLE using Display none/block

FIDDLE using Position and Height

Chrome可以快速处理100K这些行,很难看到显着的性能提升,而对于Firefox,我必须将行数减少到10K,性能提升更加明显。

答案 5 :(得分:1)

如果你想隐藏或显示所有元素,为什么不让css为你做呢?

http://jsfiddle.net/M3gye/

HTML:

<input type="button" onclick="toggle_divs();" value="Toggle"/>
<div id="container1">
    <div id="div1" class="border">div1</div>
    <div id="div2" class="border">div2</div>
    <div id="div3" class="border">div3</div>
    <div id="div4" class="border">div4</div>
    <div id="div5" class="border">div5</div>
    <div id="div6" class="border">div6</div>
    <div id="div7" class="border">div7</div>
    <div id="div8" class="border">div8</div>
    <div id="div9" class="border">div9</div>
</div>

CSS:

.border {
    border: 1px solid black;
    background-color: lightblue;
}
.hide div {
    display: none;
}

JAVASCRIPT:

function toggle_divs() {
    var d = document.getElementById('container1');
    if (d) {
        if (d.className == "hide")
            d.className = "";
        else
            d.className = "hide";
    }
}

在这个例子中,我所做的只是设置容器div的类,让选择器为我提供。