繁体   English   中英

如何对DOM元素进行排序,从而触发最少量的回流?

[英]How can I sort DOM elements triggering the least amount of reflow?

我有以下例子。

<div class="parent">
  <div data-id="5"></div>
  <div data-id="2"></div>
  <div data-id="3"></div>
  <div data-id="1"></div>
  <div data-id="4"></div>
</div>

如果我想按升序(1,2,3,4,5)订购这些div。 我通常会做一个循环并将div添加到parent div。 然而,这意味着我总是对dom进行5次更改(无论div的顺序如何),每个div一次。

但是,你可以使用.insertBefore()方法只需2次更改即可正确排序div!

5,2,3,1,4
Insert 1 before 2
5,1,2,3,4
Insert 5 behind 4 (using .insertBefore() and .nextSibling)
1,2,3,4,5 

问题1通过仅对DOM进行2次更改,我假设回流次数较少,使“2次更改”排序操作比“5次更改”操作更快。 这个对吗?

问题2什么样的方法/算法能够确定只5 behind 45 behind 4插入1 before 2

问题3(奖金)随着物品数量的增加,这种“优化”算法会更快吗? 范围10 - 100 - 1.000 - 10.000 - 100.000

也许澄清一下:不是在寻找一种以最佳方式找出顺序(1,2,3,4,5)的方法。 在某一点我知道订单,但我想再次比较div的顺序,然后THEN计算出最少的操作量

对浏览器的功能更有信心。

  1. 当浏览器在一个单一的同步执行JavaScript序列中执行时,浏览器将DOM操作批处理在一起。 当您通过访问需要DOM为最新的DOM属性/方法(例如innerWidthoffsetTopgetBoundingClientRect )来明确地(有时是在不知不觉中)请求重排时,会发生异常。 有关详细信息请参见渲染:重绘重排,重新设计。
    删除DOM节点不是必需的。 将它们添加到新创建的DocumentFragment ,它们将自动从DOM中的当前位置移除。

  2. 浏览器,更具体地说是JS引擎,已经知道并使用最智能的排序算法。 除特定情况外,您无需了解排序操作的内部机制。 只需使用Array.prototype.sort ,必要时提供自定义功能,并观察魔法发生。

从DOM中删除所有节点,并在代码中对它们进行排序后按正确的顺序替换它们。

HTML:

<div id="wrapper">
  <div id="par" class="parent">
    <div id="5">5</div>
    <div id="2">2</div>
    <div id="3">3</div>
    <div id="1">1</div>
    <div id="4">4</div>
  </div>
</div>

使用Javascript:

var clone = document.getElementById("par")
  .cloneNode(true)
var childs = [].slice.call(clone.children);
var sorted = childs.sort(function(a, b) {
  return parseInt(a.id) - parseInt(b.id)
})
var frag = document.createDocumentFragment();
sorted.forEach(function(el) {
  frag.appendChild(el)
})

var wrapper = document.getElementById("wrapper");
wrapper.removeChild(document.getElementById("par"));
wrapper.appendChild(frag);

说明:单个DOM操作比排序算法中的算法步骤重。

如果数字变大,最智能的排序算法需要O(n log n)倍的节点数。 这意味着n * log(n)DOM操作。 在人类语言中,这只是节点数量的几倍。

但是如果你只是删除所有节点,然后以正确的顺序再次添加它们,那么最坏的情况下会得到n + 1个DOM操作。 可能你可以将所有节点添加到一起,你最终会得到一个更接近2 / O(1)的数字 ,但我并不专注于现代浏览器实际完成的速度,所以让我们继续使用n + 1 ,保守地说。

现在到了数字:

假设你有5个节点。 在这种情况下,一切都很好,数字很小,但为了你自己的平安,以及你和你的同事的未来和平,你想写一个清晰的算法。 因此,删除所有节点并按正确的顺序替换它们。

现在说你有1000个节点。 在这种情况下,排序将需要大约10,000次操作(n * log n = 1000 * 10)**。 如果将每个节点分别放在那里,则将有10,000个DOM操作。

相反,如果您只是从DOM中删除节点,在javascript代码中对它们进行排序,然后将它们重新放入,那么您将只有1000个DOM操作。 这是相当保守的,因为我觉得它实际上只需要2个DOM操作:一次删除所有节点,一次以正确的顺序添加所有节点***

**我宁愿给出难以理解的数字,只是为了弄清楚这一切。 基于1024的基数2,即10 ***这可能是真正的差异所在。 如果有人知道,请评论/编辑!

请看一下这段代码。

基本上它从dom中移除容器,应用排序,将其重新插入dom。

通常这一切都可以在一帧重绘中发生,因此不会影响页面/滚动问题的长度

 +function() { /** * Finding thing wrapping class */ var $con = $('#container'); /** * getting the parent of the wrapping class so we can reinsert when we're done. */ var $parent = $con.parent(); /** * Removing container from dom so we can sort in speed */ $con.remove(); /** * Getting the things we need from container that we need to sort */ var $tosort = $con.find('.thing'); /** * Apply sorting algorything, get a newly sorted list */ var $neworder = $tosort.sort(function(a,b){ return parseInt(a.innerText) > parseInt(b.innerText); }); /** * Simply append the newly ordered list. They are the same objects, not clones so they'll fit into the right order. */ $con.append($neworder); /** * Reinsert container into the dom. */ $parent.append($con); }() 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="wrap"> <div id="container"> <div class="thing"> 4 </div> <div class="thing"> 3 </div> <div class="thing"> 1 </div> <div class="thing"> 5 </div> <div class="thing"> 2 </div> </div> </div> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM