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

原文地址:https://www.cnblogs.com/liyf-98/p/14413272.html