本篇文章主要分享一下操做DOM时的一些细节,来提升页面性能。 首先咱们来思考如下几个问题。 1.如何获取页面中全部class为div1和div2的div元素。 2.你了解HTMLCollection和NodeList吗?有什么区别? 3.如何获取一个元素的css属性值? 4.怎么减小重排和重绘?
这个问题若是我之前写的话,我会这么写。
var errs = [], divs = document.getElementsByTagName('div'), className = ''; for(var i = 0 , len = divs.length; i < len; i ++ ){ className = divs[i].className; if (className === 'div1' || className === 'div2') { errs.push(divs[i]); } }
看起来中规中矩,好像没什么大问题。 可是如今我会这么写
var errs2 = document.querySelectorAll('div.div1, div.div2');
用querySelectorAll,表面上看缩短了不少代码量,其实它的做用还不止这个。接下来咱们引出第二个问题。
咱们先来看两段代码。想一下这两段代码有什么区别。
// 片断一 var all = document.getElementsByTagName('div'); for(var i = 0; i < all.length ; i ++) { document.body.appendChild(document.createElement('div')); } // 片断二 var all2 = document.querySelectorAll('div'); for(var i = 0; i < all2.length ; i ++) { document.body.appendChild(document.createElement('div')); }
你能够分别复制一下这两段代码在你的浏览器中运行,看看有什么区别没有。前提是你的页面中至少有一个div。不然不会进入到循环中去。
运行完以后你会发现,片断一运行完以后页面崩溃了。片断二正常如期运行。为何呢?由于用getElementsByTagName方法获取到的是HTMLCollection,而HTMLCollection是实时获取的,也就是说,每次循环时请求all.length,都会从新去触发一次getElementsByTagName方法。致使all.length每次循环以后都会加1。最终形成了死循环。而用querySelectorAll方法获取到的结果是NodeList,NodeList不是实时获取的。也正是由于这个缘由,咱们推荐使用querySelector去代替getElementBy方法。实时获取会形成页面不断重排,这极大的下降了性能。另外:除了getElementByXXX方法外,document.images/document.links等方法返回的也是HTMLCollection。css
咱们知道,直接用div.style.color这样的方式去获取css样式只能获取到行内写的属性,写在style标签中的样式是获取不到的。标准浏览器中有一个window.getComputedStyle方法,能够获取实时的样式(在IE中是currentStyle)。可是这个方法也会引发页面的重排,由于只有把页面重排以后他才会获取到实时的样式。浏览器
在改变DOM的一些样式时,重排和重绘是没法避免的,可是咱们要尽可能让这个次数下降,下面介绍集中方法。
1.当须要给一个DOM元素加各类动画或者改变一系列样式时,先把这个元素设置成绝对定位。由于脱离文档流后,再对这个元素进行操做就不会影响到他周围的元素,也就不会发生重排。等到一系列复杂的操做结束以后,再把这个元素的定位恢复到以前的样子。这样只进行了两次重排,能明显的提升性能。
2.当咱们须要改变不少样式时,不要用style.color,style.width,style.height这样一个一个去改,由于每次修改都会引发一次重排。咱们应该使用给这个元素加一个类名,把全部的样式都写到这个类名中。或者是改style.cssText。这样能够有效的减小重排的次数。
3.用fragment代替dom。思考这样一个问题,如何给页面中插入10000个div元素。每次建立一个而后append到body中去吗?这显然不是一个好的办法。咱们应该先把全部建立的元素都添加到一个fragment中。而后再把fragment添加到body中去。这样只会引发一次重排,而不是1万次。
4.介绍一些会引发重排的属性。当访问元素的如下属性时也会引发重排,由于这些属性是实时算的。缓存
5.尽量少的访问DOM。看下面两段代码。app
// 片断一 console.time('test1') for(let i = 0; i < 1000; i++) { document.getElementById('div1').innerHTML+= i; } console.timeEnd('test1'); // 打印结果80ms左右 // 片断二 console.time('test3') let count = '' for(let i = 0; i < 1000; i++) { count += i; } document.getElementById('div1').innerHTML+= count; console.timeEnd('test3'); // 打印结果0.3ms左右
一个是不停的访问DOM,并修改内容,一个是先把内容缓存起来,只访问一次。打印出来时间相差好几百倍。dom
用正确的DOM属性。性能
// 由于咱们大部分时候须要操做的是元素节点,像什么文本节点,注释节点通常都会过滤掉。若是是这样的话,提倡用前面的属性代替后面的属性。 // children childNodes // childElementCount childNodes.length // firstElementChild firstChild // lastElementChild lastChild // nextElementSibling nextSibling // previousElementSibling previousSibling
须要给大量元素分发事件时,采起事件委托。由于浏览器不只不须要分发不少事件,并且须要跟踪每一个事件处理器,这也会占用更多内存。动画