什么是DOM
D(document)O(object)M(model) 文档对象模型。
MDN的解释:
DOM(文档对象模型)是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义一种方式可以使从程序中对该结构进行访问,从而改变文档的结构、样式和内容。DOM将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。
DOM中的O指的是Object,是在内存中,按照树型结构,通过构造函数(如Node,Element(翻译为标签比较
好),Document三个构造函数),构造出对象,将 DOM 展现到内存中DOM的D指的是Document,可以认为是 HTML 文档
DOM的M指的是Model,HTML结构在内存中,用一个模型来表示,这个模型就是树型结构
-
DOM就是一种想象的树形结构,它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
html 是特殊的 Element,单独有一个对象叫做Docment ,html节点又叫根节点,可以通过输入document.documentElement来获得html节点。
node
- DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。
-
html 对应的构造者是 Docment , Docment造出了 html 这个对象。
- Node 是 Docment Element Text 原型链的顶端
- 页面中的节点,通过Docment Element Text Coment 这四个构造函数构造出对应的对象这,就是DOM 的主要功能。
节点类型(nodeType)
Element类型(标签节点)
(1)nodeType为1
(2)nodeName为元素标签名,tagName也是返回标签名
(3)nodeValue为null
(4)parentNode可能是Document或Element
(5)子节点可能是Element,Text,Comment,Processing_Instruction,CDATASection或EntityReference
Text类型(文本节点)
纯文本内容,不能包含html代码
(1)nodeType为3
(2)nodeName为#text
(3)nodeValue为文本内容
(4)parentNode是一个Element
(5)没有子节点
Attr类型(属性节点)
(1)nodeType值为2
(2)nodeName是特性的名称
(3)nodeValue是特性的值
(4)parentNode为null
Comment类型
Comment表示HTML文档中的注释,它有下面的几种特征:
(1)nodeType为8
(2)nodeName为#comment
(3)nodeValue为注释的内容
(4)parentNode可能是Document或Element
(5)没有子节点
Document
Document表示文档,在浏览器中,document对象是HTMLDocument的一个实例,表示整个页面,它同时也是window对象的一个属性。Document有下面的特性:
(1)nodeType为9
(2)nodeName为#document
(3)nodeValue为null
(4)parentNode为null
(5)子节点可能是一个DocumentType或Element
检测一个元素是文字还是标签的方法:var isElement = div.nodeType === 1
文本标签的tagName是undefined
tagName会返回一个大写的标签,只有一个特例 svg 是小写
节点创建型api
createElement(创建标签节点)
vardiv = document.createElement("div");
通过createElement创建的元素并不属于html文档,它只是创建出来,并未添加到html文档中,要调用appendChild或insertBefore等方法配合使用将其添加到HTML中。
createTextNode(创建文本节点)
var textNode = document.createTextNode("一个TextNode");
createTextNode接收一个参数,这个参数就是文本节点中的文本,和createElement一样,创建后的文本节点也只是独立的一个节点,同样需要appendChild将其添加到HTML文档树中
cloneNode
cloneNode是用来返回调用方法的节点的一个副本,它接收一个布尔值参数,用来表示是否复制子元素,使用如下:
var parent = document.getElementById("parentElement");
var parent2 = parent.cloneNode(true);// 传入true
parent2.id = "parent2";
这段代码通过cloneNode复制了一份parent元素,其中cloneNode的参数为true,表示parent的子节点也被复制,如果传入false,则表示只复制了parent节点。
我们看看这个例子
我是父元素的文本
我是子元素
var parent = document.getElementById("parent");
document.getElementById("btnCopy").onclick = function(){
var parent2 = parent.cloneNode(true);
parent2.id = "parent2";
document.body.appendChild(parent2);
}
这段代码很简单,主要是绑定button事件,事件内容是复制了一个parent,修改其id,然后添加到文档中。
这里有几点要注意:
(1)和createElement一样,cloneNode创建的节点同样要调用appendChild方法才能添加到文档树中
(2)如果复制的元素有id,则其副本同样会包含该id,由于id具有唯一性,所以在复制节点后必须要修改其id
(3)调用接收的布尔值参数最好传入,如果不传入该参数,不同浏览器对其默认值的处理可能不同
除此之外,我们还有一个需要注意的点:
如果被复制的节点绑定了事件,则副本也会跟着绑定该事件吗?这里要分情况讨论:
(1)如果是通过addEventListener或者比如onclick进行绑定事件,则副本节点不会绑定该事件
(2)如果是内联方式绑定比如
这样的话,副本节点同样会触发事件。
页面修改型API
appendChild
appendChild我们在前面已经用到多次,就是将指定的节点添加到调用该方法的节点的子元素的末尾。调用方法如下:
parent.appendChild(child);
child节点将会作为parent节点的最后一个子节点。
appendChild这个方法很简单,但是还有有一点需要注意:如果被添加的节点是一个页面中存在的节点,则执行后这个节点将会添加到指定位置,其原本所在的位置将移除该节点,也就是说不会同时存在两个该节点在页面上,相当于把这个节点移动到另一个地方。我们来看例子
要被添加的节点
要移动的位置
document.getElementById("btnMove").onclick = function(){
var child = document.getElementById("child");
document.getElementById("parent").appendChild(child);
}
这段代码主要是获取页面上的child节点,然后添加到指定位置,可以看到原本的child节点被移动到parent中了。
这里还有一个要注意的点:如果child绑定了事件,被移动时,它依然绑定着该事件。
insertBefore
insertBefore用来添加一个节点到一个参照节点之前,用法如下:
parentNode.insertBefore(newNode,refNode);
parentNode表示新节点被添加后的父节点
newNode表示要添加的节点
refNode表示参照节点,新节点会添加到这个节点之前
我们来看这个例子
父节点
子元素
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新节点"
parent.insertBefore(newNode,child);
}
这段代码创建了一个新节点,然后添加到child节点之前。
和appendChild一样,如果插入的节点是页面上的节点,则会移动该节点到指定位置,并且保留其绑定的事件。
关于第二个参数参照节点还有几个注意的地方:
(1)refNode是必传的,如果不传该参数会报错
(2)如果refNode是undefined或null,则insertBefore会将节点添加到子元素的末尾
removeChild
removeChild顾名思义,就是删除指定的子节点并返回,用法如下:
var deletedChild = parent.removeChild(node);
注意:如果被删除的节点不是其子节点,则程序将会报错。我们可以通过下面的方式来确保可以删除:
if(node.parentNode){
node.parentNode.removeChild(node);
}
通过节点自己获取节点的父节点,然后将自身删除。
replaceChild
replaceChild用于使用一个节点替换另一个节点,用法如下
parent.replaceChild(newChild,oldChild);
newChild是替换的节点,可以是新的节点,也可以是页面上的节点,如果是页面上的节点,则其将被转移到新的位置
oldChild是被替换的节点
总结
(1)不管是新增还是替换节点,如果新增或替换的节点是原本存在页面上的,则其原来位置的节点将被移除,也就是说同一个节点不能存在于页面的多个位置
(2)节点本身绑定的事件会不会消失,会一直保留着。
节点查询型API
document.getElementById
根据元素id返回元素,返回值是Element类型,如果不存在该元素,则返回null。
使用这个接口有几点要注意:
(1)元素的Id是大小写敏感的,一定要写对元素的id
(2)HTML文档中可能存在多个id相同的元素,则返回第一个元素
(3)只从文档中进行搜索元素,如果创建了一个元素并指定id,但并没有添加到文档中,则这个元素是不会被查找到的
document.getElementsByTagName
根据标签名返回元素,得到的是一个集合
“*”表示所有标签
document.getElementsByName
通过指定的name属性来获取元素,它返回一个即时的NodeList对象。
使用这个接口主要要注意几点:
(1)返回对象是一个即时的NodeList,它是随时变化的
(2)在HTML元素中,并不是所有元素都有name属性,比如div是没有name属性的,但是如果强制设置div的name属性,它也是可以被查找到的
(3)在IE中,如果id设置成某个值,然后传入getElementsByName的参数值和id值一样,则这个元素是会被找到的,所以最好不好设置同样的值给id和name
document.getElementsByClassName
这个API是根据元素的class返回一个即时(表示该集合是随时变化的,也就是是文档中有几个div,它会随时进行变化,当我们新增一个div后,再访问HTMLCollection时,就会包含这个新增的div)的HTMLCollection
注意:
(1)返回结果是一个即时的HTMLCollection,会随时根据文档结构变化
(2)IE9以下浏览器不支持
(3)如果要获取2个以上classname,可传入多个classname,每个用空格相隔,例如
var elements = document.getElementsByClassName("test1 test2");
document.querySelector和document.querySelectorAll
这两个api很相似,通过css选择器来查找元素,注意选择器要符合CSS选择器的规则。
首先来介绍一下document.querySelector。
document.querySelector返回第一个匹配的元素,如果没有匹配的元素,则返回null。
注意,由于返回的是第一个匹配的元素,这个api使用的深度优先搜索来获取元素。我们来看这个例子:
第三级的span
同级的第二个div
document.getElementById("btnGet").addEventListener("click",function(){
var element = document.querySelector(".test");
alert(element.textContent);
})
两个class都包含“test”的元素,一个在文档树的前面,但是它在第三级,另一个在文档树的后面,但它在第一级,通过querySelector获取元素时,它通过深度优先搜索,拿到文档树前面的第三级的元素。
document.querySelectorAll的不同之处在于它返回的是所有匹配的元素,而且可以匹配多个选择符
class为test
id为test
document.getElementById("btnShow").addEventListener("click",function(){
var elements = document.querySelectorAll("#test,.test");
for(var i = 0,length = elements.length;i
这段代码通过querySelectorAll,使用id选择器和class选择器选择了两个元素,并依次输出其内容。要注意两点:
(1)querySelectorAll也是通过深度优先搜索,搜索的元素顺序和选择器的顺序无关
(2)返回的是一个非即时的NodeList,也就是说结果不会随着文档树的变化而变化
节点关系型API
父关系型
parentNode:每个节点都有一个parentNode属性,它表示元素的父节点。Element的父节点可能是Element,Document或DocumentFragment。
parentElement:返回元素的父元素节点,与parentNode的区别在于,其父节点必须是一个Element,如果不是,则返回null
得到一个元素的父节点的方法:
children.parentNode
children.parentElement
兄弟关系型
previousSibling:节点的前一个同级节点,如果该节点是第一个节点,则为null。注意有可能拿到的节点是文本节点或注释节点,与预期的不符,要进行处理一下。
previousElementSibling:返回前一个元素节点,前一个节点必须是Element,注意IE9以下浏览器不支持。
nextSibling:节点的后一个节点,如果该节点是最后一个节点,则为null。注意有可能拿到的节点是文本节点,与预期的不符,要进行处理一下。
nextElementSibling:返回后一个元素节点,后一个节点必须是Element,注意IE9以下浏览器不支持。
子关系型
childNodes:选出全部的孩子,它是标准属性,它返回指定元素的子元素集合,包括HTML节点,所有属性,文本节点(嫡出)
children:一个即时的HTMLCollection,子节点都是Element
firstNode:第一个子节点
lastNode:最后一个子节点
hasChildNodes方法:可以用来判断是否包含子节点。
得到一个元素的所有子代标签的方法:
1.不包含文本:div.children
2.包含文本:div.childNodes
元素属性型API
setAttribute
setAttribute:设置或更改一个元素的属性,用法:
element.setAttribute(name, value);//name属性名,value属性值
getAttribute
getAttribute返回指定的属性名相应的值,如果不存在,则返回null或空字符串。用法如下:
varvalue = element.getAttribute("id");
相关补充:
得到一个a标签里面的href的方法
a.href:带http协议的,不推荐使用
- a.getAttribute(‘href’):你写的是什么获取的就是什么,推荐使用
元素样式型API
window.getComputedStyle
我们想要获得css 的样式, box.style.left box.style.backgorundColor
但是它只能得到 行内的样式。
window.getComputedStyle可以得到的元素内嵌(使用