我有以下例子。
<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()方法正确排序div,仅 2次更改!
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次更改,我假设回流次数较少,使得&#39; 2更改&#39;排序操作比“更改”排序操作更快。这是对的吗?
问题2 只有插入1 before 2
和5 behind 4
才能找到哪种方法/算法?
问题3(奖金)这是否会优化&#39;因为物品的数量增加,算法仍然会更快?范围10 - 100 - 1.000 - 10.000 - 100.000
也许澄清一下:我不正在寻找以最佳方式找出订单(1,2,3,4,5)的方法。在某个时刻,我知道订单,但我想再次比较div的顺序,然后THEN找出最少的操作量。
答案 0 :(得分:5)
对浏览器的功能更有信心。
浏览器在一次单一同步执行JavaScript序列时执行DOM操作。当您通过访问要求DOM是最新的DOM属性/方法来明确地(有时是在不知不觉中)请求重排时,会发生异常,例如, innerWidth
,offsetTop
或getBoundingClientRect
。有关详细信息,请参阅Rendering: Repaint, Reflow, Restyle
删除DOM节点不是必需的。将它们添加到新创建的DocumentFragment
,它们将自动从DOM中的当前位置移除。
浏览器,更具体地说是JS引擎,已经知道并使用最智能的排序算法。除特定情况外,您无需了解排序操作的内部机制。只需使用Array.prototype.sort
,必要时提供自定义功能,并观察魔法发生。
答案 1 :(得分:2)
从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)的数字,但我并不专注于现代浏览器实际完成的速度,所以让#39保守地留在n + 1。
现在谈谈数字:
假设您有5个节点。在这种情况下,一切都很好,数字很小,但为了你自己的平安,以及你和你的同事的未来和平,你想写一个清晰的算法。因此,删除所有节点并按正确的顺序替换它们。
现在说你有1000个节点。在这种情况下,排序将需要大约10,000次操作(n * log n = 1000 * 10)**。如果将每个节点分别放在那里,则将有10,000个DOM操作。
相反,如果您只是从DOM中删除节点,在javascript代码中对它们进行排序,然后将它们重新放入,那么您将只有1000个DOM操作。这是非常保守的,因为我觉得它实际上只需要2个DOM操作:一次删除所有节点,一次以正确的顺序添加所有节点***
**我宁愿给出难以理解的数字,只是为了弄清楚这一切。基于1024的基数2,即10 ***这可能是真正的区别所在。如果有人知道,请评论/编辑!
答案 2 :(得分:0)
请看一下这段代码。
基本上它从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>