DOM
DOM是JS操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JS对象,从而可以用脚本进行各种操作(比如增删内容)。
•文档
–文档表示的就是整个的HTML网页文档
•对象
–对象表示将网页中的每一个部分都转换为了一个对象。
•模型
–使用模型来表示对象之间的关系,这样方便我们获取对象
文档对象模型(DOM)是网页的编程接口。它给文档(结构树)提供了一个结构化的表述并且定义了一种方式——程序可以对结构树进行访问,以改变文档的结构,样式和内容。
DOM提供了一种表述形式将文档作为一个结构化的节点组以及包含属性和方法的对象。从本质上说,它将web页面和脚本或编程语言连接起来了。
要改变页面的某个东西,JS就需要获得对网页中所有元素进行访问的入口。这个入口,连同对HTML元素进行添加、移动、改变或移除的方法和属性,都是通过DOM来获得的。
浏览器会根据DOM模型,将结构化文档(比如HTML和XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。所以,DOM可以理解成网页的编程接口。DOM有自己的国际标准,目前的通用版本是DOM 3,DOM 4。
严格地说,DOM不属于JS,但是操作DOM是JS最常见的任务,而JS也是最常用于DOM操作的语言。DOM是浏览器厂商提供的js操作html的api,不同的浏览器厂商提供的api可能不同,所以dom存在兼容性问题(少部分)
1.节点层级
任何 HTML 或 XML 文档都可以用 DOM 表示为一个由节点构成的层级结构。节点分很多类型,每种类型对应着文档中不同的信息和(或)标记,也都有自己不同的特性、数据和方法,而且与其他类型有某种关系。这些关系构成了层级,让标记可以表示为一个以特定节点为根的树形结构。以下面的 HTML为例:
<html> <head> <title>Sample Page</title> </head> <body> <p>Hello World!</p> </body> </html>
其中,document 节点表示每个文档的根节点。在这里,根节点的唯一子节点是元素,我们称之为文档元素(documentElement)。文档元素是文档最外层的元素,所有其他元素都存在于这个元素之内。每个文档只能有一个文档元素。在 HTML 页面中,文档元素始终是元素。在 XML 文档中,则没有这样预定义的元素,任何元素都可能成为文档元素。HTML 中的每段标记都可以表示为这个树形结构中的一个节点。元素节点表示 HTML 元素,属性节点表示属性,文档类型节点表示文档类型,注释节点表示注释。DOM 中总共有 12 种节点类型,这些类型都继承一种基本类型。
1.2.节点
DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
节点的类型有七种:Document、DocumentType、Element、Text、Comment、DocumentFragment。
常用节点
文档节点(document)
整个HTML文档document对象作为window对象的属性存在的,我们不用获取可以直接使用。
元素节点(Element)
HTML文档中的HTML标签。
属性节点(Attribute)
元素的属性 表示的是标签中的一个一个的属性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。
文本节点(Text)
HTML标签中的文本内容。
其他节点
DocumentType
doctype标签(比如<!DOCTYPE html>)。
Comment
注释
DocumentFragment
文档的片段
这七种节点都属于浏览器原生提供的节点对象(下面要讲的Node对象)的派生对象,具有一些共同的属性和方法。
1.3.节点树
一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是DOM。
最顶层的节点就是document
节点,它代表了整个文档。文档里面最高的HTML标签,一般是<html>
,它构成树结构的根节点(root node),其他HTML标签节点都是它的下级。
除了根节点以外,其他节点对于周围的节点都存在三种关系。
- 父节点关系(parentNode):直接的那个上级节点。
- 子节点关系(childNode):直接的下级节点。
- 同级节点关系(sibling):拥有同一父节点的节点。
DOM提供操作接口,用来获取三种关系的节点。其中,子节点接口包括firstChild
(第一个子节点)和lastChild
(最后一个子节点)等属性,同级节点接口包括nextSibling
(紧邻在后的那个同级节点)和previousSibling
(紧邻在前的那个同级节点)属性。
2.Node类型
DOM Level 1 描述了名为 Node 的接口,这个接口是所有 DOM 节点类型都必须实现的。Node 接口在 JavaScript中被实现为 Node 类型,在除 IE之外的所有浏览器中都可以直接访问这个类型。在 JavaScript中,所有节点类型都继承 Node 类型,因此所有类型都共享相同的基本属性和方法。
2.1.属性
2.1.1.nodeType
nodeType属性返回一个整数值,表示节点的类型,常用节点类型如下
节点类型 | 值 | 对应常量 |
文档节点(document) | 9 | Node.DOCUMENT_NODE |
元素节点(element) | 1 | Node.ELEMENT_NODE |
属性节点(attr) | 2 | Node.ATTRIBUTE_NODE |
文本节点(text) | 3 | Node.TEXT_NODE |
文档类型节点(DocumentType) | 10 | Node.DOCUMENT_TYPE_NODE |
注释节点(Comment) | 8 | Node.COMMENT_NODE |
文档片断节点(DocumentFragment) | 11 | Node.DOCUMENT_FRAGMENT_NODE |
<script> console.log(document.nodeType); //9 </script>
2.1.2.nodeName
nodeName属性返回节点的名称
<div id="d1">hello world</div> <script> var div = document.getElementById('d1'); console.log(div.nodeName); //DIV </script>
2.1.3.nodeValue
nodeValue属性返回一个字符串,表示当前节点本身的文本值,该属性可读写只有文本节点(text)、注释节点(comment)和属性节点(attr)有文本值.
<div id="d1">hello world</div> <script> var div = document.getElementById('d1'); console.log(div.nodeValue); // null // 读 console.log(div.firstChild.nodeValue); //hello world // 写 div.firstChild.nodeValue = '123'; </script>
2.1.4.textContent
textContent属性返回当前节点和它的所有后代节点的文本内容
<div id="d1">Hello <span>JavaScript</span> DOM</div> <script> var div = document.getElementById('d1'); console.log(div.textContent); //Hello JavaScript DOM </script>
2.1.5.nextSibling
nextSibling属性返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回null
(注意:可能会获取到“空格”或“回车”这样的文本节点)
<div id="d1">hello</div><div id="d2">world</div> <script> var div1 = document.getElementById('d1'); var div2 = document.getElementById('d2'); console.log(div1.nextSibling); //<div id="d2">world</div> console.log(div1.nextSibling === div2); // true </script>
2.1.6.previousSibling
previousSibling属性返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回null
<div id="d1">hello</div><div id="d2">world</div> <script> var div1 = document.getElementById('d1'); var div2 = document.getElementById('d2'); console.log(div2.previousSibling); //<div id="d1">hello</div> console.log(div2.previousSibling === div1); // true </script>
2.1.7.parentNode
parentNode属性返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment)
<div id="d1">hello world</div> <script> var div1 = document.getElementById('d1'); console.log(div1.parentNode); // body </script>
2.1.8.parentElement
parentElement属性返回当前节点的父元素节点。如果当前节点没有父节点,或者父节点类型不是元素节点,则返回null
<div id="d1">hello world</div> <script> var div1 = document.getElementById('d1'); console.log(div1.parentElement); // body // 将父元素节点的背景颜色设置为红色 div1.parentElement.style.backgroundColor = 'red'; </script>
2.1.9.firstChild和lastChild
firstChild属性返回当前节点的第一个子节点,如果当前节点没有子节点,则返回null,last则返回最后一个子节点。
<div id="d1">hello world<div>我是子节点</div></div> <div id="d2"><div>我是子节点</div></div> <script> var div1 = document.getElementById('d1'); console.log(div1.firstChild); // hello world console.log(div1.lastChild); // <div>我是子节点</div> var div2 = document.getElementById('d2'); console.log(div2.firstChild); // <div>我是子节点</div> </script>
2.1.10.childNodes
childNodes属性返回一个类似数组的对象(NodeList集合),成员包括当前节点的所有子节点
<div id="d1">hello world<div>我是子节点</div></div> <script> var div1 = document.getElementById('d1'); console.log(div1.childNodes); //NodeList[text, div] </script>
我们还可以使用for循环来遍历某个节点的所有子节点
var div = document.getElementById('div1'); var children = div.childNodes; for (var i = 0; i < children.length; i++) { // ... }
2.2.方法
以下方法为常用操作节点的方法,其中最常用的方法是appendChild(),用于在 childNodes 列表末尾添加节点。
注意:以下四个方法都需要父节点对象进行调用!
2.2.1.appendChild
appendChild方法接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点。
<script> // 创建元素节点p var p = document.createElement('p'); // 向p标签插入内容 p.innerHTML = '我是一个p标签'; // 将节点插入到body中 document.body.appendChild(p); </script>
2.2.2.insertBefore()
insertBefore方法用于将某个节点插入父节点内部的指定位置。
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
insertBefore方法接受两个参数,第一个参数是所要插入的节点newNode,第二个参数是父节点parentNode内部的一个子节点referenceNode。newNode将插在referenceNode这个子节点的前面。返回值是插入的新节点newNode
<div id="parentElement"> <span id="childElement">foo bar</span> </div> <script> //创建一个新的、普通的<span>元素 var sp1 = document.createElement("span"); // 向span标签插入内容 sp1.innerHTML = '我是span标签' //插入节点之前,要获得节点的引用 var sp2 = document.getElementById("childElement"); //获得父节点的引用 var parentDiv = sp2.parentNode; //在DOM中在sp2之前插入一个新元素 parentDiv.insertBefore(sp1, sp2); </script>
2.2.3.removeChild()
removeChild方法接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点。
<div id="d1"> <span id="s1">我是span标签</span> </div> <script> var span1 = document.getElementById('s1'); span1.parentNode.removeChild(span1); </script>
2.2.4.replaceChild()
replaceChild方法用于将一个新的节点,替换当前节点的某一个子节点。
var replacedNode = parentNode.replaceChild(newChild, oldChild);
replaceChild方法接受两个参数,第一个参数newChild是用来替换的新节点,第二个参数oldChild是将要替换走的子节点。返回值是替换走的那个节点oldChild。
<div id="d1"> <span id="s1">我是span标签</span> </div> <script> var span1 = document.getElementById('s1'); //创建一个新的div标签 var div1 = document.createElement("div"); // 向div标签插入内容 div1.innerHTML = '我是div1标签'; // 节点替换 span1.parentNode.replaceChild(div1, span1); </script>
2.2.5.其他方法
cloneNode()
方法返回调用该方法的节点的一个副本.
var dupNode = node.cloneNode(deep);
node
将要被克隆的节点
dupNode
克隆生成的副本节点
deep 可选
是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身。
var p = document.getElementById("para1"), var p_prime = p.cloneNode(true);