0. DOM简介
任何一种技术都是有它存在的意义,DOM正是为了方便操作HTML文档而诞生的技术。在没有DOM的时代,为了操作HTML元素曾发展出了DHTML(动态HTML),后来由于微软和Netscape的浏览器大战而销声匿迹。DOM脱胎于DHTML,并于1998年DOM 1级成为了W3C的推荐标准。现在利用JavaScript操作DOM已经成为基础前端技术,被各大浏览器完善支持。
DOM(Document Object Model,文档对象模型),是一种针对HTML和XML文档编程API。DOM将整个HTML或XML文档映射为一个层次化的节点树,用以节点的增删改查操作,以达到改变页面的目的。每个HTML或XML元素都可以看成一个节点,每个节点都有对应的数据和层次结构,使用JavaScript等脚本语言可以对节点的进行操作。
例如如下HTML文档:
<html>
<head>
<title>learnDOM</title>
</head>
<body>
<p>hello</p>
<h1>world</h1>
</body>
</html>
可将此HTML文档表示为节点的层次结构:
其实html上册还有一个根节点:文档节点。html节点称为文档元素,任何一个HTML文档只有一个文档元素,因此html节点能表示整个文档。整个DOM树是一种典型的树状数据结构,例如body节点拥有两个子节点p和h1,文本节点"learnDOM"父节点为title。
1. 节点类型
每个节点都有一个nodeType属性来表明节点类型。Node类型定义了12个类型,由数值常量1~12来表示。常用节点类型有:
节点类型 | 数值常量 | 字符常量 |
---|---|---|
Element(元素节点) | 1 | ELEMENT_NODE |
Attr(属性节点) | 2 | ATTRIBUTE_NODE |
Text(文本节点) | 3 | TEXT_NODE |
Comment(注释节点) | 8 | COMMENT_NODE |
Document(文档节点) | 9 | DOCUMENT_NODE |
DocumentType(文档类型节点) | 10 | DOCUMENT_TYPE_NODE |
DocumentFragment(文档片段节点) | 11 | DOCUMENT_FRAGMENT_NODE |
1)Element(元素节点),是组成节点树的重要部分,通常拥有子元素、文本节点等子节点,也是唯一能拥有属性的节点类型,例如:
都是元素节点。
2)Attr(属性节点),是元素节点的一部分,不能单独作为节点出现在节点树中。例如:
<div id="addr"></div>
中id就是元素节点div的属性节点。
3)Text(文本节点),只包含文本内容的节点,在XML中也称为字符数据,可为空字符。节点树中,元素的文本内容和属性的文本内容都是由文本节点来表示的。例如:
中"learnDOM"就是文本节点。
4)Comment(注释节点),表示文档中注释的内容。
5)Document(文档节点),是文档的根节点。在HTML文档的节点树中处于html节点之上的节点为文档节点。
6)DocumentType(文档类型节点),每个文档都有一个类型属性,值或为空,或为DocumentType对象。在HTML中:
<!DOCTYPE html>
就是文档类型节点。
7)DocumentFragment(文档片段节点),是文档的片段,并不属于文档节点树。例如:
var frag = document.createDocumentFragment();
frag就为一个文档片段,可对其操作后写入文档树,文档片段就起了类似临时变量的作用。
接下来举一个判断节点类型的例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>learnDOM</title>
</head>
<body>
<div id="addr">元素节点</div>
<script>
var divNode = document.getElementById("addr");
if (divNode.nodeType === Node.ELEMENT_NODE) {
console.log("A element node");
}
</script>
</body>
</html>
将div节点的id"addr"传入getElementById()方法,得到一个节点元素divNode。判断divNode的类型可通过上述if语句。注意IE中只支持与数值常量,ELEMENT_NODE这种字符常量是无法识别的。上例中通过节点元素的nodeType属性判断节点类型,除此之外常用属性还有nodeName和nodeValue。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>learnDOM</title>
</head>
<body>
<!-- learnNodeType -->
<div id="addr">元素节点</div>
<script>
//元素节点
var divNode = document.getElementById("addr");
console.log(divNode.nodeName); //div
console.log(divNode.nodeValue); //null
//属性节点
var attrNode = divNode.attributes[0];
console.log(attrNode.nodeName); //id
console.log(attrNode.nodeValue); //addr
//文本节点
var textNode = divNode.childNodes[0];
console.log(textNode.nodeName); //#text
console.log(textNode.nodeValue);//元素节点
//注释节点
var commentNode = document.body.childNodes[1];//注释前空白为[0]
console.log(commentNode.nodeName);//#comment
console.log(commentNode.nodeValue);//learnNodeType
//文档类型节点
console.log(document.doctype.nodeName); //html
console.log(document.doctype.nodeValue);//null
//文档片段节点
var frag = document.createDocumentFragment();//创建文档片段
console.log(frag.nodeName); //#document-fragment
console.log(frag.nodeValue); //null
</script>
</body>
</html>
2. DOM方法和属性
- getElementById(),返回指定ID的元素
- getElementsByTagName(),返回指定标签名称的所有元素节点集合
- getElementsByClassName(),返回指定类名的所有元素节点集合
- appendChild(),添加新节点到指定节点
- removeChild(),删除子节点
- replaceChild(),替换子节点
- insertBefore(),在指定节点前插入新节点
- createAttribute(),创建属性节点
- createElement(),创建元素节点
- createTextNode(),创建文本节点
- getAttribute(),返回指定属性值
- setAttribute(),修改指定属性值
- innerHTML,节点文本值
- parentNode,节点的父节点
- childNodes,节点的子节点
- attributes,节点的属性节点
2.1 DOM获取
<h1 id="country">countries</h1>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
<p class="AS">KR</p>
</div>
<script>
//获取ID为"country"的节点
var countries = document.getElementById("country");
//先定位ID为"div_list"的节点,后获取其内部所有p节点
var ps = document.getElementById("div_list").getElementsByTagName("p");
//先定位ID为"div_list"的节点,后获取其内部所有class为EU的节点
var eus = document.getElementById("div_list").getElementsByClassName("EU");
//获取div_list所有子节点,第一个子节点,最后一个子节点
var divTest = document.getElementById("div_list");
var divTestChilds = divTest.childNodes;
var firstChild = divTest.firstChild;
var lastChild = divTest.lastChild;
</script>
或者用querySelector()和querySelectorAll(),注意IE8+版本才有支持。具体用法见https://developer.mozilla.org/zh-CN/docs/Web/API/Document/2.2 DOM修改
//获取节点
var h = document.getElementById("country");
//设置文本
h.innerHTML = "hello";
//设置HTML
h.innerHTML = "hello <span style=\"color:red\">red</span> ";
var divTest = document.getElementById("div_list");
divTest.style.color = "#00ff00";
divTest.style.fontSize = "10px";
2.3 DOM删除
//获取待删除节点
var self = document.getElementById("div_list");
//获取父节点
var parent = self.parentNode;
//删除
var removed = parent.removeChild(self);
console.log(removed === self); //true
若使用父节点的children属性进行遍历删除,注意children属性是只读的,并且随着操作会实时更新: <div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
<p class="AS">KR</p>
</div>
<script>
var parent = document.getElementById("div_list");
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[5]);//error,节点只剩了五个,children[5]不存在
</script>
2.4 DOM插入
<div id="blank"></div>
<script>
var d = document.getElementById("blank");
d.innerHTML = "hello";
</script>
但是节点非空时会将原内容覆盖。 <p id="kr">KR</p>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
</div>
<script>
var list = document.getElementById("div_list");
var kr = document.getElementById("kr");
list.appendChild(kr);
</script>
<p id="kr">KR</p>被插入到list最后,并且原位置的kr会被删除,相当于完成了节点的移动。我们也能从零创建一个新节点并插入父节点中:
var list = document.getElementById("div_list");
var newNode = document.createElement("p");
newNode.id = "kr";
newNode.innerHTML = "KR";
list.appendChild(newNode);
<p id="kr">KR</p>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
</div>
<script>
var list = document.getElementById("div_list");
var ref = list.children[2];
var node = document.getElementById("kr");
list.insertBefore(node, ref);
</script>