fast interacting with large number of elements inside a container (DOM, javascript)

守給你的承諾、 提交于 2019-12-04 19:25:38

问题


So I have a large number of divs (4000-5000) [each contains spans, anchors, images etc.] inside a container div and basically I am setting their display to none or block based on a condition. This does take some time.

In my search for something faster I came across this page https://developers.google.com/speed/articles/javascript-dom and the solution there is to remove the container div from the DOM and iterate the contained elements by 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();
}

The problem is I cannot use the solution provided because I need to access the children elements by their IDs and I can't do that, since the elements are removed from the DOM. Is there any way to achieve this?

I also tried to remove the container div and append it to a documentfragment, but still I can't access the 5000 elements by their ID when they are in the documentfragment

Finally, I also tried this:

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

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

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

because I was hoping that it would not trigger a reflow for each iteration, but this didn't seem to provide an improvement in the time required.

Does anyone have any thoughts on this?


回答1:


Display none/block is expensive. From my days of improving performance on trading web platforms, one technique I can recommend that'll shine especially with older browsers is to use position relative and yank it off the screen using a negative left value. Of course depending on your implementation, you might want to set the height to 0px as well or look into possibilities with position absolute. The core concept still remains that you are simply yanking the element off the screen. Good news is that the elements hidden are still in the DOM and you can access them.

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

Check out these two fiddles, they create 10K rows and toggle(hide/show) the odd rows:

FIDDLE using Display none/block

FIDDLE using Position and Height

Chrome handles 100K of these rows in a snap and it's difficult to see a significant performance improvement, whereas for Firefox I had to reduce the row count to 10K and the performance gain is much more apparent.




回答2:


I will try to provide sources as requested.

First solution - best one
According to this site: JavaScript Grid with One Million Records
You can learn several important things:

  1. Large number of DOM nodes make rendering slow
  2. JavaScript arrays can handle large data sets
  3. Looping through large arrays is fast
  4. Sorting arrays by providing custom function to Array.sort() is fast
  5. eval() is slow, should not be used in large loops

So, I would recommend you to build an array to handle in a fast way your elements.

Second solution
Another solution taken from this site: Processing large amounts of data in JavaScript
would be to use a timeout (as strange as it sounds) to increase speed of handler.
The idea comes from Book: Secrets of the JavaScript Ninja




回答3:


If you want only to show/hide, without change anything in the div's DOM and you know all the IDs, I think, that the best (fastest) way to archive this would be to prepare <style /> element and append it to DOM. Style el should contain all the ID's and proper display. Iterate through IDs and add it to CSS string, then create <style /> element and append string to it. This should work for you.




回答4:


Build an id-to-element map / hash table beforehand:

var map = {};

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

where ids is a list of element IDs. (If you need to 5000 elements by their IDs, I assume you have a list or can generate one.)

Then when you remove the container element from the DOM, you can use the map to find elements by their IDs.




回答5:


The following two statements are equivalent (the second approach requires the # but is available on all elements, not just the document).

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

So you can use element.querySelector('#theId') to get to a child element based on its ID (even if the parent element is currently detached from DOM).

Note that querySelector() is not supported in IE 7 and older.




回答6:


If you want to hide or show all elements, why not let css do it for you?

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";
    }
}

In this example, all I do is set the class of the container div and let the selectors to the rest for me.



来源:https://stackoverflow.com/questions/22195771/fast-interacting-with-large-number-of-elements-inside-a-container-dom-javascri

工具导航Map

JSON相关