对Dom的访问代价是昂贵,在富网页应用中通常是性能的瓶颈,所以对Dom的优化十分重要。

一.访问和修改Dom元素

浏览器通常要求JavaScript和Dom实现保持独立的。例如,在Internet Explorer 中,被称为JScript的JavaScript 实现位于库文件jscript.dll 中,而DOM 实现位于另一个库mshtml.dll(内部代号Trident)。所以用JavaScript访问Dom元素是需要一定的代价。人们通常这样形容Dom访问,javaScript和Dom相当于两个独立的小岛,这两个小岛中间有座桥连起来,JavaScript每次访问Dom都要过一次桥,过桥就要收取一定的过桥费。所以要尽量减少访问Dom的次数,那么如何做到呢?请看下面的例子。

  function innerHTMLLoop() {
    for (var count = 0; count < 15000; count++) {
      document.getElementById('here').innerHTML += 'a';
    }
  }

上面这个函数的作用是对id是here这个Dom节点,进行了15000次修改,在这个函数中,对Dom节点进行了15000次访问。再看下面的函数。

  function innerHTMLLoop() {

var text="";
    for (var count = 0; count < 15000; count++) {
       text+= 'a';
    }

document.getElementById('here').innerHTML=text;
  }

上面这个函数和第一个函数实现的是完全一样的功能,但是它对Dom的访问只有一次,性能是大大提高。

现在来比较一下document.createElement和innerHTML。

document.createElement是每次创建一个节点,然后加入到父节点中。innerHTML是用字符串拼接成一系列Dom节点然后在插入到目标节点中。如果在一个性能苛刻的操作中更新一大块HTML 页面,innerHTML 在大多数浏览器中执行更快。但对于大多数日常操作而言,其差异并不大,所以你应当根据代码可读性,可维护性,团队习惯,代码风格来综合决定采用哪种方法。

另外HTML集合用于存放Dom节点类数组对象,例如document.getElementsByTagName,getElementsByClass等函数得到的就是集合。它的使用也是非常昂贵的。考虑下面的例子

  var alldivs = document.getElementsByTagName_r('div');
  for (var i = 0; i < alldivs.length; i++) {
    document.body.appendChild(document.createElement('div'))
  }

上面的例子表面上是想倍增div的数量,但实际上是个死循环,因为div的长度是动态变化的,也就是说alldivs.length不是一个常量,它会每次根据div的数量变化,这样也就是每次要访问Dom节点,我们之前说过访问Dom的代价是昂贵的。所以最好的方法是将length用一个变量缓存起来,例如len=alldivs.length。

 二.重绘和重排

我们都知道在页面刚加载的时候,浏览器会进行页面渲染,其实这就是一次绘制和排列的过程。绘制的是页面,排列的是Dom节点,一般来说绘制是根据color,background,opacity等css属性,而排列则根据width,height,margin,padding等能影响页面Dom元素位置变换的css属性。那么既然知道了什么是重绘和重排,就应该知道什么时候会重会和重排。也就是当你改变某些css属性时。

一般来说重排的影响是远大于重绘的,重排时浏览器会重新计算所有元素的位置和尺寸,重新排列。这个过程可以说是相当耗时间的,所以要尽量避免重排。考虑下面例子

  var el = document.getElementById('mydiv');
  el.style.borderLeft = '1px';
  el.style.borderRight = '2px';
  el.style.padding = '5px';

上面例子三次改变了el的css属性,而这三次都产生的重排。再看下面代码

var el = document.getElementById('mydiv');

el.style.cssText="border-left:1px;border-right:2px;padding:5px";

上面代码实现的是和第一例子一样的功能,但是它只产生的一次重排,大大提高了性能。

 三.事件托管

  当页面中存在大量元素,而且每个元素有一个或多个事件句柄与之挂接(例如onclick)时,可能会影响性能。连接每个句柄都是有代价的,无论其形式是加重了页面负担(更多的页面标记和JavaScript 代码)还是表现在运行期的运行时间上。所以事件托管是很有用的。

每个事件发生时会经历捕获,到达目标,冒泡,三个阶段。例如:

 

  当用户点击了“menu #1”链接,点击事件首先被<a>元素收到。然后它沿着DOM 树冒泡,被<li>元素收到,然后是<ul>,接着是<div>,等等,一直到达文档的顶层,甚至window。如果需要在每个li上加监听事件,那么可以加到ul上,因为每次点击li都会冒泡到ul上。

  总结:

DOM 访问和操作是现代网页应用中很重要的一部分。但每次你通过桥梁从ECMAScript 岛到达DOM 岛时,都会被收取“过桥费”。为减少DOM 编程中的性能损失,请牢记以下几点:

最小化DOM 访问,在JavaScript 端做尽可能多的事情

在反复访问的地方使用局部变量存放DOM 引用.  

  小心地处理HTML 集合,因为他们表现出“存在性”,总是对底层文档重新查询。将集合的length 属性缓存到一个变量中,在迭代中使用这个变量。如果经常操作这个集合,可以将集合拷贝到数组中。 
    注意重绘和重排版;批量修改风格,离线操作DOM 树,缓存并减少对布局信息的访问。

使用事件托管技术最小化事件句柄数量。

最新文章

  1. 要慎用mysql的enum字段的原因
  2. STM32启动代码分析 IAR 比较好
  3. 【Mail】搭建邮件服务器(LAMP+Postfix+Dovcot+PostfixAdmin+Roundcubemail)
  4. Mac OS 下安装wget
  5. 第十一篇 SQL Server代理维护计划
  6. java ee 中文乱码的问题
  7. 安卓向服务器发送List数据
  8. MySql5.7-多源复制(多主单从)
  9. C++之linux下文件结构实现
  10. 链接与ELF文件格式的复习
  11. android电池充电以及电量检测驱动分析
  12. SpringCloud系列十:SpringCloudConfig 高级配置(密钥加密处理(JCE)、KeyStore 加密处理、SpringCloudConfig 高可用机制、SpringCloudBus 服务总线)
  13. Pfsense2.34中文版
  14. 37.Linux驱动调试-根据oops的栈信息,确定函数调用过程
  15. Access时间日期函数大全
  16. distinct count
  17. Django:上传文件或者图片时request.FILES的值为空
  18. 《C++ Primer》读书笔记(二)-变量和基本类型
  19. Office安装错误1402的解决
  20. java打包成window service服务[转]

热门文章

  1. 微信小程序の模板
  2. Mysql差集
  3. C# JavaScriptSerializer 自定义序列化
  4. CSP 2019 模板整合
  5. Java里的参数类型/返回值类型
  6. shiro实现用户踢出功能
  7. JavaScript常见设计模式梳理
  8. phpStudy的安装和配置
  9. luoguP2148 [SDOI2009]E&amp;D [sg函数][组合游戏]
  10. 浏览器表单默认记忆功能input的 autocomplete=&quot;off&quot;属性