zoukankan      html  css  js  c++  java
  • DOM

    DOM基础

    文档对象模型

    1)DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。

    ​ 它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。

    2)浏览器将所有html的标签都放入到window.document这个对象中,利用这个对象,可以对所有的标签进 行操作,一般来说就是增删改查。

    3)浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成 一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。所以,DOM 可以 理解成网页的编程接口。

    4)在内存中,存的不是html树,是一棵对应html各个节点的对象树,而且对象树的节点是与html树的节点 一一对应的。页面中的节点,根据Element、Text、Document、Comment这些构造函数,构造出对象 来,内存就理解了。

    ​ DOM就是完整的把Document和Object映射到一起,符合DOM规范的结构,所以具备很多的API。

    Node

    DOM是一棵树,树上有Node,Node分为Document、Element、Text、...

    Node是一个接口,许多DOM类型从这个接口继承,并允许类似地处理(或测试)这些各种类型。

    • 以下接口都从Node继承其方法和属性: Document, Element, CharacterData (which Text, Comment, and CDATASection inherit), ProcessingInstruction, DocumentFragment, DocumentType, Notation, Entity, EntityReference

    Node的属性

    DOM树的最小单位就是节点(node)。

    文档的树形结构就是有各个不同列类型的节点组成的,每个节点都可以看做是这棵DOM树的叶子.

    node之间的关系属性

    其他节点与周围节点的关系

    1. parentNode: 直接的那个上级的节点
    2. childNodes: 直接的下一级的节点
    3. sibling:拥有同一个父节点的节点 next & previous

    node自身的属性

    1. nodeName

    2. nodeType

    3. nodeValue

    选取元素

    名称选择

    根据ID、name属性、标签名称、类名;来选择对应的DOM节点。

    //ID选择器:基于id=""
    let id =  document.getElementById("id");
    //名称选择器:基于name属性
    let name = document.getElementsByName("name");
    //标签选择器:利用HTML元素的标签名称选取指定类型的元素
    var h1 = document.getElementsByTagName("h1");
    //类选择器:利用HTML的class属性值选择元素
    let title = document.getElementsByClassName(title);
    
    

    通过ID获取——元素对象

    • document是获取元素的上下文(获取元素的范围)
    • 获取到的结果是一个对象(堆内存:里面存储着很多内置的属性和方法)
    1. getElementById 方法的上下文只能是document
    • div举栗子,divHTMLDivElement类的一个实例,documentHTMLDocument 的实例。
    • 他们的继承关系:
      • HTMLDivElement > HTMLElement > Element > Node > EventTarget
      • HTMLDocument > Document > Node > EventTarget

    我们都知道子类继承父类,子类就可以使用父类的属性和方法

    • 他们相同的继承关系是NodeEventTarget,也就是说他们都可以使用NodeEventTarget上的方法。
    • Node上的nodeNameparentNode等,和EventTarget上的addEventListener等。
    • getElementById只在Document类的原型上,HTMLDivElement 没有继承Document类,所以div不能使用getElementById方法。

    后面要说到的getElementsByTagName方法则是即在Document类的原型上也在Element类的原型上,所以divdocument都可以使用getElementsByTagName方法。

    CSS选择

    通过CSS样式表选择器的语法,也可以来选择元素,返回第一个匹配的元素,或者返回元素数组

    var title = document.querySelector("#title");  
    // CSS ID选择
    var h1 = document.querySelector("h1");        
    //选取第一个h1元素
    //通过Element类型调用时,只会在该元素后代元素的范围内查找匹配的元素。
    var h1s = document.querySelectorAll("h1");   
    //返回所有h1标签元素
    //返回的是一个Nodelist实例。
    

    获取特定元素

    1. 获取html:document.documentElement

    2. 获取所有元素:document.all

      • document.all是出自ie浏览器,以前的程序员为了知道用户是不是用的ie,会写这样一段代码

      • if(document.all){
        	这里写的代码兼容ie
        } else{
        	这里写的代码是为其他浏览器所写
        }
        
      • 后来所有浏览器都沿用了document.all这个API,但是用户的代码没有变。按照逻辑,那么所有程序都会运行兼容ie的代码,这是有悖于浏览器厂商的意愿的,最终大家都达成共识,在浏览器中,document.all就代表着falsy值

        • falsy值即为boolean上下文中的false 如0、null...
    3. 获取head元素:document.head

    4. 获取body元素:document.body

    相近节点选取

    节点:

    页面中所有的内容都是节点(标签,属性,文本:文字,空格,换行)

    文档:

    document -> 页面中的顶级对象元素

    页面中所有的标签,标签--元素--对象(通过DOM的方式来获取这个标签得到了这个对象,此时这个对象叫DOM对象)。

    关于节点的选取有如下的方法:

    h1.parentNode; 
    //父节点
    h1.childNodes; 
    //以数组形式返回子节点
    h1.firstChild; 
    //第一个子节点
    h1.lastChild;
    //最后一个子节点
    h1.nextSibling; 
    //下一个兄弟节点
    h1.previousSibling; 
    //前一个兄弟节点
    h1.nodeType;
    //返回节点类型的数字表示:
    // 1-element节点;3-text节点;8-comment节点;9-document节点;11-documentFragment节点
    h1.nodeValue; 
    //返回Text 节点 或 Comment 节点的值
    h1.nodeName; 
    //返回元素的标签名,以大写形式表示
    
    
    

    元素相关的选取同样有如下的方法:

    h2.children;
    //以数组的形式返回所有的子元素
    h2.firstElementChild; 
    h2.lastElementChild;
    //返回首子元素与尾子元素
    h2.nextElementSibling; 
    h2.previousElementSibling;
    //返回上一兄弟元素与下一兄弟元素
    h2.childElementCount;
    //返回子元素数量
    
    

    childNodes & children

    元素 & 节点

    contains方法

    某个节点是不是另一个节点的后代

    调用contains()方法的应该是祖先节点,也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点,该方法返回true;否则,返回false。

    document.documentElement.contains(document.body);   // true
    
    

    属性相关

    表示HTML文档元素的HTMLElement对象定义了读/写属性,它们对应于元素的HTML属性。

    HTMLElement定义的通用HTML属性,包括id、lang、dir、事件处理程序onclick及表单相关属性等。

    h3.getAttribute("width");
    //返回非标准的HTML属性的值
    h3.setAttribute("width", "150px");
    //设置非标准的HTML属性的值
    h3.createAttribute("class");
    //创建一个属性节点
    h3.hasAttribute("height");
    //判断属性是否存在
    h3.removeAttribute("width");
    //删除某一属性
    h3.dataset.x;
    //在HTML5文档中,任意以 data- 为前缀的小写的属性名字都是合法的。
    //这些 “数据集属性” 定义了一种标准的、<附加额外数据>的方法
    //以data-x = ""为例
    let a = h3.attributes.src.value;
    //Node节点定义了 attributes 属性,针对 Element 对象,attributes 是元素所有属性的类数组对象
    //索引 attributes 对象得到的值是 Attr 对象。Attr 的 name 和 value 返回该属性的名字和值
    h4.innerHTML;
    //以字符串形式返回这个元素的内容。 也可以用来替换元素当前内容
    h4.outerHTML;
    //以字符串形式返回这个元素及内容。 也可以用来替换元素当前内容
    h4.textContent;
    //查询或替换纯文本元素内容的标准方法是用Node的textContent属性来实现。
    
    

    data-x

    attributes

    image-20210218124249425

    attributes.key.value

    image-20210218124427687

    Property & Attribute

    1)在js线程中的所有属性,为Property

    2)在css线程中的属性,为Attribute

    大部分时间这两者相等,但是Attribute肯定是字符串。而property支持字符串或者布尔值、数字等类型

    innerHTML & outerHTML

    1)innerHTML:
      从对象的起始位置到终止位置的全部内容,不包括Html标签。
    2)outerHTML:
      除了包含innerHTML的全部内容外, 还包含对象标签本身。

    创建节点

    document.createElement("h1");
    //使用document 对象的createElement () 方法创建新的Element节点
    document.createTextNode("文本节点");
    //创建纯文本节点
    document.createDocumentFragment();
    //创建文档片段,往往有更好性能
    //因为文档片段存在于内存中,并不在Dom树中,所以将子元素插入到文档片段时不会引起页面回流 (对元素位置和几何上计算)
    document.createCmoment("....");
    //创建注释节点
    h4.cloneNode(true);
    //通过复制已存在的节点来创建新的文档节点。传参数true表示深克隆,false表示浅复制
    
    

    	        console.log(h1 == h1Copy)  // false
    

    插入、修改节点

    h5.appendChild("h1");
    //在指定元素上插入子节点,并使其成为该节点的最后一个子节点
    //一般先新建子节点,再插入子节点
    h5.insertBefore("h1", "h2");
    //1. 在父节点上调用本方法
    //2. 第一参数表示待插入的节点
    //3. 第二参数是父节点中已经存在的子节点,新节点插入到该节点的前面
    h5.removeChild("h2");//在父节点中调用,参数是待删除的节点
    h5.replaceChild("h2,", "h2");
    //1. 在父节点上调用;
    //2. 第一参数是新节点;
    //3. 第二个参数是需要替换的节点
    
    

    注意: insertbefore()中的参数都为节点 而非元素

    修改class

    div.className = 'red' 
    // 这里的修改会覆盖原来的className
    
    // HTML5新增了一种操作类名的方式,可以让操作更简单也更安全,那就是为所有元素添加classList属性。
    div.classList.add('red')
    // 这个classList属性是新集合类型DOMTokenList的实例。
    // DOMTokenList有一个表示自己包含多少元素的length属性,而要取得每个元素可以使用item()方法,也可以使用方括号语法。此外这个新类型还定义如下方法。
    
    add(value): 
    // 将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
    contains(value): 
    // 表示列表中是否存在给定的值,如果存在则返回true,否则返回false。
    remove(value): 
    // 从列表中删除给定的字符串
    toggle(value): 
    // 如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。
    
    

    修改style

    div.style='200px' 
    // 这里会覆盖原来的style
    div.style.backgroundColor ='red' 
    // 修改style的指定部分,注意大小写,DOM编程基本遵循css的样式,但是background-color的-不会被使用,而是用大小写的写法
    
    

    修改自定义data-属性

    div.dataset.x='qiu'
    使用js填写自定义属性使用.setAttribute('data-xxx','属性值')
    读取属性可以使用.getAttribute('data-xxx')
    

    document & window

    1. document指页面。document是window的一个对象属性。

    2. window 指窗体。window 对象表示浏览器中打开的窗口

    3. 如果文档包含框架(frameiframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象。

    4. 用户不能改变 document.location(因为这是当前显示文档的位置)。

    但是,可以改变window.location (用其它文档取代当前文档)window.location本身也是一个对象,而document.location不是对象。

    window

    所有的全局函数和对象都属于 window 对象的属性和方法

    window是一个全局对象,可以从浏览器中运行的任何JS代码直接访问。

    window暴露了很多属性和方法,如:

    window.alert('Hello world'); 
    // Shows an alert
    window.setTimeout(callback, 3000); 
    // Delay execution
    window.fetch(someUrl); 
    // make XHR requests
    window.open(); 
    // Opens a new tab
    window.location; 
    // Browser location
    window.history; 
    // Browser history
    window.navigator; 
    // The actual user agent
    window.document; 
    // The current page
    # 因为这些属性和方法也是全局的,所以也可以这样访问它们
    alert('Hello world'); // Shows an alert
    以此类推
    
    

    焦点管理

    document.activeElement属性,这个属性始终引用了DOM中当前获得了焦点的元素。

    元素获得焦点的方式有页面加载、用户输入和在代码中调用focus()方法。

    var dom = document.getElementById('dom');
    
    dom.focus();
    
    document.activeElement === dom;  // true
    dom.hasFocus();  // true
    

    DOM 级别

    以下针对事件部分

    1)DOM0 -- 标准之前

    2)DOM1 -- 标准 DOM core + DOM HTML

    3)DOM2 -- 引入很多模块 包括 DOM events

    4)DOM3 -- 没有对事件做任何修订

    DOM事件机制

    1、事件是在编程时系统内发生的动作或者发生的事情

    2、事件是要绑定在元素上的。比如给一个div元素绑定一个鼠标悬浮事件,给一个ol元素绑定鼠标单击事件。

    3、可以使用事件监听函数(也叫事件处理程序、侦听器来监听事件,以便事件发生时执行相应的代码

    事件发生时元素节点之间按照特定的顺序传播,这个过程即DOM事件流,描述的是从页面接收事件的顺序。

    冒泡与捕获

    首先开始事件捕获阶段:

    从DOM树最根部的节点window开始,沿着DOM树向下遍历每个元素,直到触发元素目标元素target。

    如果这些元素也注册了click事件(且为捕获阶段),就会执行他们相应的事件监听函数。

    即从上到下触发父元素对应的事件。在事件捕获这一阶段,为截获事件提供了机会。

    当前目标阶段:

    实际的目标接收到,并执行对应得事件监听函数。

    事件冒泡阶段:

    从触发元素目标元素target开始,向上逆着遍历DOM树,直到最根部window元素。

    如果这些元素也注册了click事件(且为冒泡阶段),就会执行他们相应的事件监听函数

    addEventListener

    addEventListener('click', fn, bool)
    如果第三个参数bool不传,或者传false, 那么我们会在冒泡阶段调用fn
    如果第三个参数bool传值为true, 那么我们会在捕获阶段调用fn
    因此,默认是在冒泡阶段来监听事件的。
    

    捕获不可以取消,但是冒泡可以取消,e.propagation()就可以取消冒泡。

    但是有一些事件不可以取消冒泡,比如 scroll 事件。

    e.target & e.currentTarget

    e.target 用户正在操作的元素

    e.currentTarget 程序员在监听的元素

    this就是e.currentTarget

    事件委托

    js中的事件函数都是对象,如果事件函数过多会占用大量内存,而且绑定事件的DOM元素越多会增加访问dom的次数,对页面的交互就绪时间也会有延迟。所以诞生了事件委托.

    事件委托就是利用事件冒泡,只需指定一个事件处理程序,就可以管理某一类型的所有事件,通过事件委托,可以做到通过在祖先元素添加一个事件处理程序,就可以控制其子孙元素的某些行为。

    冒泡阶段,浏览器从用户点击的内容从下往上遍历至 window,逐个触发事件处理函数,

    因此可以监听一个祖先节点(例如父亲节点)来同时处理多个子节点的事件。

    有函数监听就调用,并提供事件信息。没有就跳过。

    主要的作用有:

    1、省掉监听数,节省内存;只监听最外层元素,然后在事件处理函数中根据事件源,即target属性,进行不同的事件处理。元素的事件会冒泡到最外层,被最外层的事件处理程序截获。

    2、监听不存在的元素,即动态元素。

    var ul=document.getElementById('ulList');
    ul.onclick=function(e){
        var e= e || window.event;
        var target = e.target || e.srcElement;
        if(target.nodeName.toLowerCase() === "aaa"){
            alert("我是"+e.target);
        }
    }
    // 从这个例子可以看出,当用事件委托的时候,完全不需要遍历元素的子节点,只需要给父级元素添加事件监听就好了,之后新添加的子节点也能够同样的对触发事件作出适当的响应
    
    // 还有一个常见的利用事件委托的例子,就是点开浮层,关闭浮层,我们常常利用事件委托来监听元素外空间区域的点击,来关闭浮层。
    
    

    事件处理函数

    默认事件处理方法的api为null。

    改事件处理函数

    div.onclick = function() {
      alert('click')
    }
    // 当点击后,浏览器会帮助我们调用这一个callback回调函数
    // 用的方法是fn.call(div,event)
    // div代表要调用的对象,event包含了点击事件中的所有信息,例如坐标x。
    

    事件处理程序

    事件处理程序就是响应某个事件的函数,DOM中的事件处理程序有多种方式,大概可以分为以下三种类型。

    1. HTML事件处理程序

      <button onclick="alert(hello world!)"></hello>
      <!-- 执行的代码实际上是由JS引擎由eval()调用的,所以它是全局作用域。-->
      <!-- 这样的事件处理有一个明显的缺点,即当JS代码太复杂时,将大段JS代码卸载HTML中显然不合适,于是有了下面这种写法:-->
      <button onclick="doSomething()"></hello>
      <!-- 这样虽然解决了嵌套代码过长的问题,但又引来了另一个问题,即时差问题—,如果用户在界面刚出现就进行点击,而JS还没有加载好的话,就会报错。
        此外,很重要的一点是,这种写法,一个函数的改变,可能同时需要js和html的改变,严重违背了轻耦合的原则,综上,我们有了DOM0级事件处理。-->
      
      
    2. DOM0级事件处理程序

      <script>
      var btn=document.getElementById("#btn");
      btn.onclick=function(){
        alert(hello world!)
      }
      </script>
      // 这种方式中可以把事件处理相关部门都放到js中,
      // 并且这里的事件处理程序是作为btn对象的方法的,是局部作用域。
      // 但是如果有对这个元素的单击事件添加两个处理函数,一不小心可能会覆盖他人之前对这个元素的该事件添加的处理函数
      
    3. DOM2级事件处理程序

      # 不支持ie
      // 进一步规范后,有了DOM2级事件处理程序,我们可以通过类似如下代码,对一个元素的同一个事件添加多个处理程序
      var btn=document.getElementById("#btn");
      btn.addEventListener("click",function(){
         alert(hello world!)
      })
      
      btn.addEventListener("click",function(){
         alert(hello world2!)
      })
      </script>
      # 同样的事件和事件流机制下相同的方法只会触发一次,即相同的方法会发生覆盖。
      // 事件程序可能会在两个阶段中被执行,即捕获中和冒泡中,当一个事件添加了两个处理函数,一个指定了参数true,一个指定的参数false,则它们都会被执行,且参数为true的那个先执行,因为是捕获阶段先发生.
      // 如果事件函数被添加在了目标元素本身上,被绑定了两个单击事件函数,一个第三个参数是true,一个第三个参数是false,则它们的实际执行顺序是不受第三个参数控制的,而只是单纯的和添加事件的顺序有关(先addEventListener的先执行),
      
      

    IE事件处理程序

    attachEvent & detachEvent

    在IE9之前,必须使用attachEvent而不是标准方法addEventListener,
    IE事件处理程序中有类似于DOM2级事件处理程序的2个方法attachEvent和detachEvent

    它们都接收两个参数:

    事件处理程序名称,如 onclick,onmounseover,
    注意,这里是事件处理程序名称,而不是事件名称,要有前缀on

    事件处理程序函数

    不像DOM2级事件处理程序一样,它们不接收第三个参数,

    因为IE8及更早版本只支持冒泡事件流(没有捕获阶段)
    IE8中,事件执行的顺序不是添加的顺序而是添加顺序的相反顺序

    而在IE6,7中 事件执行的顺序是随机的,和添加顺序无关。
    使用attachEvent方法还有个缺点是,this的值会变成window对象的引用而不是触发事件的元素。

    跨浏览器的事件处理程序

    var EventUtil={
        addEventHandler: function(type,element,handler){
            if(element.addEventListener){
                element.addEventListener(type,handler,false);
            }else if(element.attachEvent){
                element.attachEvent("on"+type,element);
            }else{
                element["on"+type]=handler;
            }
        },
        removeEventHandler: function(type,element,handler){
            if(element.removeEventListener){
                element.removeEventListener(type,handler,false);
            }else if(element.detachEvent){
                element.detachEvent("on"+type,element);
            }else{
                element["on"+type]=null;
            }
        }
    }
    
    

    事件对象

    事件对象是用来记录一些事件发生时的相关信息的对象,但事件对象只有事件发生时才会产生,并且只能在事件处理函数内部访问,在所有事件处理函数结束后,事件对象会被销毁。

    标准的Event对象属性主要有以下几个:

    1)bubbles 布尔值,表示事件是否是冒泡类型

    2)cancelable 布尔值,表示事件是否可以取消默认动作

    3)currentTarget 当前目标元素,即添加当前事件处理程序的元素

    4)target 实际目标元素,即实际触发事件的元素

    5)type 返回当前事件的名称

    6)eventPhase 事件传播的当前阶段,1表示捕获阶段

    标准的Event对象的方法主要有以下几个:

    1)preventDefault()

    ​ 通知浏览器不要执行该事件的默认动作,常用于阻止链接的跳转,表单的提交,等标签的默认行为

    2)stopPropagation()

    ​ 冒泡阶段下,阻止事件的继续向上冒泡

    事件对象的兼容性

    1. 事件对象的获取
    function getEvent(event){
        event = event || window.event
    }
    function hander(event){
        event = getEvent(event)
        ...
    }
    
    
    1. 阻止默认事件行为

      // IE浏览器的event事件没有preventDefault()这个方法,但是可以通过设置event的returnValue值为false来达到同样的效果,如下:
      window.event.returnValue=false;
      
    2. 阻止冒泡

      // IE浏览器的event对象也没有stopPropagation()方法,但可以设置cancelBubble属性为true,阻止事件的继续传播,如下:
      window.event.cancelBubble=true; 
      

    元素的坐标

    不是给事件对象用的,而是给元素自己用的,获取的是元素自己相对于页面(父盒子)、可视区和被卷去的距离

    DOM优化

    重绘和重排

    1)重绘是指一些样式的修改,元素的位置和大小都没有改变;

    产生重绘的因素:

    • 改变visibility、outline、背景色等样式属性,并没有改变元素大小、位置等。浏览器会根据元素的新属性重新绘制。

    2)重排是指元素的位置或尺寸发生了变化,浏览器需要重新计算渲染树,而新的渲染树建立后,浏览器会重新绘制受影响的元素。

    产生重排的因素:

    • 内容改变
    • 文本改变或图片尺寸改变
    • DOM元素的几何属性的变化
      • 例如改变DOM元素的宽高值时,原渲染树中的相关节点会失效,浏览器会根据变化后的DOM重新排建渲染树中的相关节点。如果父节点的几何属性变化时,还会使其子节点及后续兄弟节点重新计算位置等,造成一系列的重排。
    • DOM树的结构变化
      • 添加DOM节点、修改DOM节点位置及删除某个节点都是对DOM树的更改,会造成页面的重排。浏览器布局是从上到下的过程,修改当前元素不会对其前边已经遍历过的元素造成影响,但是如果在所有的节点前添加一个新的元素,则后续的所有元素都要进行重排。
    • 获取某些属性
      • 除了渲染树的直接变化,当获取一些属性值时,浏览器为取得正确的值也会发生重排,这些属性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()。
    • 浏览器窗口尺寸改变
      • 窗口尺寸的改变会影响整个网页内元素的尺寸的改变,即DOM元素的集合属性变化,因此会造成重排。
    • 滚动条的出现(会触发整个页面的重排)

    js是单线程的,重绘和重排会阻塞用户的操作以及影响网页的性能,当一个页面发生了多次重绘和重排比如写一个定时器每500ms改变页面元素的宽高,那么这个页面可能会变得越来越卡顿,要尽可能的减少重绘和重排。那么对于DOM的优化也是基于这个开始。

    优化

    减少访问

    减少访问次数自然是想到缓存元素,但是要注意

    var ele = document.getElementById('ele');
    
    

    这样并不是对ele进行缓存,每一次调用ele还是相当于访问了一次id为ele的节点。

    缓存NodeList

    var foods = document.getElementsByClassName('food');
    
    

    我们可以用foods[i]来访问第i个class为food的元素,不过这里的foods并不是一个数组,而是一个NodeList。NodeList是一个类数组,保存了一些有序的节点并可以通过位置来访问这些节点。

    NodeList对象是动态的,每一次访问都会运行一次基于文档的查询。所以我们要尽量减少访问NodeList的次数,可以考虑将NodeList的值缓存起来。

    // 优化前
    var lis = document.getElementsByTagName('li');
    
    for(var i = 0; i < lis.length; i++) {
         // do something...  
    }
    
    // 优化后,将length的值缓存起来就不会每次都去查询length的值
    var lis = document.getElementsByTagName('li');
    
    for(var i = 0, len = lis.length; i < len; i++) {
         // do something...  
    }
    由于NodeList是动态变化的,所以如果不缓存可能会引起死循环,比如一边添加元素,一边获取NodeList的length。
    

    改变选择器

    获取元素最常见的有两种方法,

    getElementsByXXX()和queryselectorAll(),

    这两种选择器区别是很大的,前者是获取动态集合,后者是获取静态集合

    对静态集合的操作不会引起对文档的重新查询,相比于动态集合更加优化。

    减少重绘重排

    改变一个dom节点的多个样式

    想改变一个div元素的宽度和高度,在css里写一个class,这样就达到了一次操作多个样式

    批量修改dom节点样式

    dom树和渲染树的区别:

    样式为display:none;的节点而不在渲染树中

    如果一个节点的display属性为none那么这个节点不会存在于render树中,会在DOM树中

    意味着对这个节点的操作也不会影响render树进而不会引起重绘和重排,基于这个思路我们可以实现优化:

    • 将待修改的集合的父元素display: none;
    • 之后遍历修改集合节点
    • 将集合父元素display: block;

    DocumentFragment

    createDocumentFragment()方是用了创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点。

    它可以包含各种类型的节点,在创建之初是空的。

    DocumentFragment节点不属于文档树,继承的parentNode属性总是null。

    它有一个很实用的特点,当请求把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点。 这个特性使得DocumentFragment成了占位符,暂时存放那些一次插入文档的节点.

    另外,当需要添加多个dom元素时,如果先将这些元素添加到DocumentFragment中,再统一将DocumentFragment添加到页面,会减少页面渲染dom的次数,效率会明显提升。

    以上部分内容来自

    掘金-苔痕阶绿 掘金-SwordQiu 掘金-前端小智 掘金-LT_bear 掘金-吴少666

    掘金-windlany

  • 相关阅读:
    CSS 之 @media
    How to fix “Duplicate sources.list entry …” issue
    shell脚本加不加export的区别
    过滤部分错误信息,不输出到stderr
    /dev/null 2>&1 解释(转)
    crontab与环境变量
    PHP实现斐波那契数列非递归方法
    有反斜杠时候,CakePHP往pgsql插入数据异常
    PHP输出图片文件,实现浏览器缓存机制
    sudo: unable to resolve host XXX 解决方法
  • 原文地址:https://www.cnblogs.com/liyf-98/p/14413272.html
Copyright © 2011-2022 走看看