资讯专栏INFORMATION COLUMN

DOM 深入学习 - 2

YuboonaZhang / 2467人阅读

摘要:使用局部变量来访问集合元素访问任何类型的,当同一个属性或者方法被访问同一次以上时,最好使用一个局部变量缓存该成员。当遍历一个集合的时候,第一个要优化的就是将集合引用存储在局部变量中,并在循环之外缓存属性。

  

本文章记录本人在深入学习Javascirpt DOM中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。

减少 DOM 重绘和重排的次数

简单了解重绘和重排

在渲染dom树中为每个需要显示的dom树节点存放至少一个节点,隐藏的dom元素在渲染树中没有对应的节点。然后将页面的元素看作一个具有填、边距、边框和位置的盒子,也就是经常提到的盒模型。如果dom树和渲染树构造完毕,浏览器就会显示或者说是绘制页面的上的元素了。

dom改变影响了元素的几何属性(宽和高)时候,如改变边距宽度或者在段落中添加文字将发生一系列后续动作,浏览器就会重新计算元素的几何属性,而且其他元素的几何属性和位置也会因此收到影响。浏览器是渲染树上受到影响的部分失效,然后重构渲染树,这个过程叫做重排。当重排完毕后,浏览器会在绘制进程中重新绘制屏幕上受到影响的部分。

有一些属性修改是不会影响几何属性的,例如:改变一个元素的背景色不会影响它的高度和宽度。这种情况,只需要重绘,因为元素的布局没有发生变化。

什么情况下会触发重绘和重排

添加或删除可见的dom元素。

元素的位置改变。

元素的尺寸改变。

内容改变,如文本改变或图片被另一个尺寸的图片所代替。

最初的页面渲染。

浏览器窗口大小改变。

某一些改变可能导致重排整个页面,例如:当一个滚动条出现时。

如何减少重排和重绘

由于重排与重绘的代价较高,提高程序响应速度是一个好策略是减少此类的操作发生的机会。

例1,应该将多个dom风格改变后合并到一个批次中一次性执行。

var el = document.getElementById("demo");
el.style.broderLeft = "1px";
el.style.broderRight = "2px";
el.style.padding = "5px";

上面的代码改变了3个样式属性,每次改变都会影响元素的几何属性。导致浏览器重排了3次。现在大多数浏览器都优化了这种情况,只进行一次重排,但是在旧版本的浏览器中,效率非常的底下。

实现相同效果但效率更高的方法:将所有改变合并在一起执行,只修改dom一次。使用cssText

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

还有另外一种优化方式就是修改css的类名称,而不是修改元素的内联风格代码。这种方法适合用于那些风格不依赖于运行逻辑且不需要计算的情况下。改变后的css类名称更加清晰,更便于维护。

el.className += " active";

例2,当需要对dom元素进行多次的修改时,可以通过以下步骤减少重绘和重排次数。

从文档流中摘除该元素。

对其应用多重改变。

将元素带回文档中。

这个过程中引发两次重排:第1步引发一次,第3步引发一次。如果忽略了这两个步骤,那么第2步中每次改变都会引发一次重排。

经历3步后可以将dom从文档中摘除:

隐藏元素

使用一个文档片断在一存在dom之外创建一个子树,然后将它复制到文档。

将原始文档复制到一个脱离文档的节点中,修改副本,然后覆盖原始元素。

避免大部分重排

重排有时候只影响渲染数的一小部分,但也能影响一大部分。甚至整个渲染树。浏览器需要重排ud部分越小。应用程序的响应速度越快,因此,当一个页面顶部的动画推移差不多整个页面时,将引发巨大的重排动作,使用户感到动画不流畅。渲染数的大多数节点需要重新计算,这使情况更加糟糕。

以下步骤可以避免大部分页面进行重排:

使用绝对定位坐标定位页面动画元素,使它位于页面布局流之外。

启动元素动画,当它扩大时候,将会临时覆盖部页面。这是一个重绘过程,但是只能影响页面一小部分,避免重排以及重绘一大块也页面。

当动画结束时候,重新定位。

克隆节点与创建节点哪个好

使用dom方法更新页面内容的另一个方法就是克隆已有的dom元素,而不是创建新的元素。在大多数浏览器上,克隆节点更有效率,但是提高不是很多。如果用克隆节点的方法去创建1000行的表格,只创建一次单元格,然后重复的执行复制操作,这样会快一些。

所以说:克隆节点与创建节都差不多,而克隆节点方法更加适合那些重复操作同一个dom元素。

使用 nextSibling 抓取 DOM

我们经常需要从一个dom元素开始,操作周围的元素,或者递归迭代所有的子节点。这时可以使用childNodes集合或者使用nextSibling获得每个元素的兄弟节点。

var testNextSibling = function(){
    var el = documentgetElementById("demo"), ch = el.firstChild, name = "";
    do {
        name = ch.nodeName; 
    } while (ch = ch.nextSibling);

    return name;
};

var testChildNodes = function(){
    var el = documentgetElementById("demo"), ch = el.childNodes, len = ch.length, name = "";
    for (var i = 0; i < len; i++) {
        name = ch[i].nodeName;
    }
    return name;
};

比较上面两个功能相同的函数,它们都采用非递归方式遍历一个元素的子节点。在不同的浏览器下,这两个函数运行的时间都基本相同,但是在IE下,nextSibling表现比childNode好。在IE6下,nextSiblingchildNode块16倍,在IE7下,nextSibling块105倍。所以说,在旧版本的IE下使用nextsibling抓取dom是首选,在其他的情况下,主要看个人和团队的习惯决定。

使用局部变量来访问集合元素

访问任何类型的dom,当同一个dom属性或者方法被访问同一次以上时,最好使用一个局部变量缓存该dom成员。当遍历一个集合的时候,第一个要优化的就是将集合引用存储在局部变量中,并在循环之外缓存length属性。然后,如果在循环体中多次访问同一个集合元素,那么使用局部变量缓存它。

// bad
var collectionGlobal = function(){
    var coll = document.getElementsByTagName("div"), len = coll.length, name = "";
    for (var i = 0; i < coll; i++) {
        name = document.getElementsByTagName("div")[i].nodeName;
        name = document.getElementsByTagName("div")[i].nodeType;
        name = document.getElementsByTagName("div")[i].tagName;
    }
    return name;
};

// good
var collectionGlobal = function(){
    var coll = document.getElementsByTagName("div"), len = coll.length, name = "", el = null;
    for (var i = 0; i < coll; i++) {
        el = coll[i];
        name = el.nodeName;
        name = el.nodeType;
        name = el.tagName;
    }
    return name;
};
使用 querySelectorAll

使用css选择器是一个便捷的确定节点的方法。许多js库提供类似的API,而且最新的浏览器提供了一个名为querySelectorAll的元素浏览器dom函数。显然这种方法比使用jsdom迭代并缩小元素列表的方法要快。

var el = document.querySelectorAll("#demo a");

当需要联合查询的时候,使用querySelectorAll方法更加的便利。例如,在页面中,一些div元素的class名称是demo1,另一些class名称是demo2,可以使用querySelectorAll一次性获得这两类节点。

var el = document.querySelectorAll(".demo1, .demo2");

如果浏览器支持querySelectorAll方法的话,那么最好就使用它。。还有另一个函数querySelector获取节点,它可以返回符合查询条件的第一个节点。

使用 DOM 结构树托管事件

一个简单而优雅的处理dom事件的技术是事件托管(事件代理)。它基于这样一个事实:事件逐层冒泡总能被父级捕获。采用事件托管技术后,只需要在一个包装元素上连接一个句柄,用与处理子元素发生的所有事件。

捕获

达到目标

冒泡

  

因为IE不支持事件捕获,所以只要实现托管技术使用冒泡就足够了。

事件托管技术就是只要监听事件侦测事件是不是从目标元素中发出的。里面会有一些跨浏览器的代码,如果将它们移入一个可复用的库里面,那么代码就会变的很干净。跨浏览器部分包括:

访问事件对象,并判断事件缘(目标)。

结束文档树上冒泡。

阻止默认动作(可选)。

HTML: 


  • 我是第1个li
  • 我是第2个li
  • 我是第3个li
JS: var oUl = document.getElementById("demo"); oUl.addEventListener("click", function(e){ // 兼容 ie var e = e || window.event, target = e.target || e.srcElement; alert(target.innerHTML); });

  

最后,如果文章有什么错误和疑问的地方,请指出。与sf各位共勉!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85709.html

相关文章

  • DOM 深入学习 - 1

    摘要:本文章记录本人在深入学习中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。级年月,正式发布更新后的核心部分,并且在这次发布添加了一些新的规范,这次发布的称为级规范。 本文章记录本人在深入学习Javascirpt DOM中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。 DOM 版本 w3c 指定的DOM规范包括多个版本,不同的版本(或称知为...

    姘存按 评论0 收藏0
  • 你需要的前端进阶书籍清单,分享下载

    摘要:写在前面目前专注深入学习,特花了点时间整理了一些前端学习相关的书籍。大致分为以下大系列系列系列基础系列应用系列进阶系列类库系列框架系列。这些书籍在这里免费提供下载,有兴趣的一起学习。 写在前面 目前专注深入JavaScript学习,特花了点时间整理了一些前端学习相关的书籍。 大致分为以下7大系列:CSS系列、DOM系列、JavaScript基础系列、JavaScript应用系列、Ja...

    yuanzhanghu 评论0 收藏0
  • 深入react技术栈》学习笔记(一)初入React世界

    摘要:前言以深入学习技术栈为线索,记录下学习的重要知识内容。要传入时,必须使用属性表达式。如果要使用自定义属性,要使用前缀这与标准是一致的。 前言 以《深入学习react技术栈》为线索,记录下学习React的重要知识内容。本系列文章没有涵盖全部的react知识内容,只是记录下了学习之路上的重要知识点,一方面是自己的总结,同时拿出来和在学习react的人们一块分享,共同进步。 正文 一:rea...

    verano 评论0 收藏0
  • 深入react技术栈》学习笔记(三)漫谈React

    摘要:前言接下来让我们进入新的章节漫谈。正文一事件系统的事件系统事件系统符合标准,不存在任何兼容性问题,并且与原生的浏览器事件一样有同样的接口。所有的事件都自动绑定到最外层。组织事件冒泡的行为只适用于合成系统中,且没办法阻止原生事件冒泡。 前言 接下来让我们进入新的章节:漫谈React。本篇文章主要讲React事件系统和表单操作。 正文 一:事件系统 1.react的事件系统react事件系...

    isLishude 评论0 收藏0
  • 深入react技术栈》学习笔记(二)初入React世界

    摘要:用于规范的类型与必需的状态。表示由组件更改的数据,通常是通过与用户的交互来更改的。为了实现的修改,需要注册事件处理程序到相应的元素上。当事件发生时,将更新后的值是从中检索,并通知组件。通常情况下,该函数初始化状态使用,,或其他数据存储。 前言 上一篇文章中,我们讲到了JSX的一些用法和注意事项,这次我们来讲react中最基础也是特别重要的内容:组件。这篇文章包含组件的以下内容:状态、属...

    MRZYD 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<