高性能javaScript(三)——DOM编程

访问和操作DOM是现代Web应用的重要部分。但每次穿越连接ECMAScript和DOM两个岛屿之间的桥梁,都会被收取“过桥费”。为了减少DOM编程带来的性能损失。

  • 最小化DOM访问次数,尽可能在JavaScript端处理。 (只适用于IE6之前的版本,新版本浏览器DOM方法更优)
 //多次调用DOM的API,不妥 
  function innerHTMLLoop() {
      for(var count = 0; count < 15000; count++) {
          document.getElementById('here').innerHTML += 'a';
      }
  }
  
 // 使用以下方法代替,只调用一次DOM的API,尽可能将在JavaScript端处理后再赋值 
  function innerHTMLLoop2() {
      var content = '';
      for(var count = 0; count < 15000; count++) {
          content += 'a';
      }
      document.getElementById('here').innerHTML += content;
  }
  • 使用节点克隆element.cloneNode()(element表示已有节点)比document.createElement()稍快。
  • 如果需要多次访问某个DOM节点,请使用局部变量存储它的引用。
  • 小心处理HTML集合,因为它实时联系着底层文档。把集合的长度缓存到一个变量中,并在迭代中使用它。如果需要经常操作集合,建议把它拷贝到一个数组中。

以下方法返回的是一个集合:

  • document.getElementsByName()
  • document.getElementsByClassName()
  • document.getElementsByTagName()

以下属性同样返回HTML集合

  • document.images
  • document.links
  • document.forms
  • document.forms[0].elements
  //以下代码会造成死循环,因为每次访问alldivs.length时都是实时的DOM信息
  var alldivs = document.getElementsByTagName('div');
  for(var i = 0; i < alldivs.length; i++) {
      document.body.appendChild(document.createElement('div'));
  }
  
  //将HTML集合拷贝到数组中使用
  function toArray(coll) {
      for(var i = 0,a = [],len = coll.length; i < len; i++) {
          a[i] = coll[i];
      }
      return a;
  }
  
  //访问集合的length比普通数组的length慢很多,所以也可以将集合的长度保存到变量中,用于循环判定
  function loopCacheLengthCollection() {
      var coll = document.getElementsByTagName('div'),
      len = coll.length;
      for(var count = 0; count < len; count++) {
          /* 代码处理 */
      }
  }

  • 如果可能的话,使用速度更快的API,比如querySelectorAll()和firstElementChild。

在老版本IE中nextSibling比childNode快。
老API不区分元素节点和其他节点,比如注释和文本节点。但在某些情况下只需访问元素节点,所以使用以下新API代替旧API

新属性名 被代替的属性
children childNodes
childElementCount childNodes.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling
  • 要留意重绘和重排;批量修改样式时,“离线“操作DOM树,使用缓存,并减少访问布局信息的次数。

改变页面布局和几何属性会触发重排。但是浏览器一般会通过队列化修改并批量执行来优化重排。但是获取布局信息会导致队列刷新,如offsetTop,scrollTop,cilentTop等

批量修改DOM的方法:

  1. 使文档脱离文档流。
  2. 对其应用多重改变。
  3. 把元素带回文档中。

三种方法:

  • 隐藏元素,应用修改,重新显示。
  • 使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
  • 将原始元素拷贝到一个脱离文档的节点,修改副本,完成后再替换原始元素。
//更新指定节点数据的通用函数:
function appendDataToElement(appendToElement, data) {
    var a, li;
    for(var i = 0,max = data.length;i < max; i++) {
        a = document.createElement('a');
        a.href = data[i].url;
        a.appendChild(document.createTextNode(data[i].name));
        li = document.createElement('li');
        li.appendChild(a);
        appendDataToElement.appendChild(li);
    }
}

//第一种方法
var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul,data);
ul.style.display = 'block';

//第二种方法,文档片段只会添加其内容而不会添加其本身。
var fragment = document.createDocumentFragment();
appendDataToElement(fragment,data);
document.getElementById('mylist').appendChild(fragment);

//第三种方案
var old = document.getElementById('mylist');
var clone = old.cloneNode(true);
appendDataToElement(clone,data);
old.parentNode.replaceChild(clone,old);
  • 动画中使用绝对定会,使用拖放代理。

使用以下步骤可以避免页面中的大部分重排:

  1. 使用绝对定位页面上的动画元素,将其脱离文档流。
  2. 让元素动起来。当它扩大时,会临时覆盖部分页面。但这只是页面一个小区域的重绘过程,不会产生重排并重绘页面大部分内容。
  3. 当动画结束时恢复定位,从而只会下移一次文档的其他元素。
  • 使用事件委托来减少事件处理器的数量。
//对li的父标签ul统一绑定事件委托
document.getElementById('menu').onclick = function(e) {
    // 浏览器target
    e = e || window.events;
    var target = e.target || e.srcElement;
    
    var pageid,hrefparts;
    
    //只关心hrefs,非链接点击则退出
    if(target.nodeName !== 'A') {
        return;
    }
    
    //从链接中找出页面ID
    hrefparts = target.href.split('/');
    pageid = hrefparts[hrefparts.length - 1];
    pageid = pageid.replace('.html','');
    
    //更新页面
    ajaxRequest('xhr.php?page=' + id, updatePageContents);
    
    //浏览器组织默认行为并取消冒泡
    if(typeof e.preventDefault === 'function') {
        e.preventDefault();
        e.stopPropagation();
    } else {
        e.returnValue = false;
        e.cancelBubble = true;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容