JavaScript 中的 HTML DOM 操作

1. 简介

DOM(Document Object Model,文档对象模型),是一个 HTML、XML 和 SVG 的编程接口,它将结构化表示的文档映射为一棵树。DOM 定义了一些允许访问这棵树的方法,因此这些方法可以改变文档结构、样式以及内容。

DOM 以节点和对象的结构化组提供文档的结构化表现,拥有各种属性和方法。节点也可以绑定事件句柄,一旦事件被触发,事件句柄将被调用执行。本质上它是将网页链接到脚本或是编程语言上。

尽管通常会使用 JavaScript 来访问 DOM, 但它并不是 JavaScript 的一部分,它也可以被其他语言使用。DOM被设计成独立于任何编程语言,通过单一的、一致的 API,可以访问结构化表现的文档。

2. HTML DOM 树

HTML 文档为结构化文档内容,HTML DOM 定义了访问和操作 HTML 文档的标准,也就是关于如何获取、修改、添加或删除 HTML 元素的标准。

在 HTML DOM 中,所有事物都是节点。DOM 是被视为节点树的 HTML。

HTML DOM 树

通过 HTML DOM,树中的所有节点均可通过 JavaScript 进行访问。所有 HTML 元素(节点)均可被修改,也可以创建或删除节点。

节点树中的节点彼此拥有层级关系。父(parent)、子(child)和兄弟(sibling)等术语用于描述这些关系:父节点拥有子节点,同级的子节点被称为兄弟(或姐妹)。

在节点树中,顶端节点被称为根(root),每个节点都有父节点、除了根(它没有父节点)。一个节点可拥有任意数量的子,兄弟节点是拥有相同父节点的节点。

下面的图片展示了节点树的一部分,以及节点之间的关系:

DOM 节点关系

常见 HTML DOM 节点类型:

  • 文档节点:整个文档 (document)
  • 元素节点:HTML 元素
  • 文本节点:HTML 元素内的文本
  • 属性节点:HTML 元素的属性
  • 注释节点:HTML 注释

3. HTML DOM 方法

常用的 HTML DOM 方法有:

方法 描述
getElementById() 返回带有指定 id 的元素
getElementsByTagName() 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)
getElementsByClassName() 返回包含带有指定类名的所有元素的节点列表
appendChild() 把新的子节点添加到指定节点
removeChild() 删除子节点
replaceChild() 替换子节点
insertBefore() 在指定的子节点前面插入新的子节点
createAttribute() 创建属性节点
createElement() 创建元素节点
createTextNode() 创建文本节点
getAttribute() 返回指定的属性值
setAttribute() 把指定属性设置或修改为指定的值

3.1 查找元素

根据 id 查找元素

1
element = document.getElementById(id);

该方法根据特定的 id 查找页面中的元素,返回的是一个元素对象。如果在页面中没有查找到对应的元素,该方法会返回null。

注意 id 参数是大小写敏感的,所以 document.getElementById("Main")无法获取到元素 <div id="main">,因为 ‘M’ 和 ‘m’ 是不一样的。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
</head>
<body>
	<input type="text" id="txt" value="测试文本">
	<script type="text/javascript">
		var element = document.getElementById("txt");
		console.log(element.value);
	</script>
</body>
</html>

根据标签名查找元素

1
var elements = document.getElementsByTagName(name);

该方法返回一个包括所有给定标签名称的元素的 HTML 集合 HTMLCollection,整个文档结构都会被搜索,包括根节点。返回的 HTML 集合是动态的,意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用 document.getElementsByTagName() 。

参数 name 是一个代表元素的名称的字符串,可以使用特殊字符 “*” 代表所有元素。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
</head>
<body>
	<span>第一个span</span>
	<input type="text" id="txt" value="测试文本">
	<div>第一个div</div>
	<span>第二个span</span>
	<div>第二个div</div>
	<span>第三个span</span>
	<script type="text/javascript">
		var elements = document.getElementsByTagName("div");
		console.log(elements);
	</script>
</body>
</html>

该方法也可以通过某个具体的元素对象来调用:

1
var elements = element.getElementsByTagName(tagName);

这时,getElementsByTagName() 方法仍然返回一个动态的包含指定标签名的元素的 HTML 集合,只是是在指定的元素的子树中搜索。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
</head>
<body>
	<div id="first">
		<div>第一个内嵌div</div>
		<span>第一个内嵌span</span>
		<div>第二个内嵌div</div>
		<span>第二个内嵌span</span>
	</div>
	<script type="text/javascript">
		var _firstDiv = document.getElementById("first");
		var elements = _firstDiv.getElementsByTagName("div");
		console.log(elements);
	</script>
</body>
</html>

根据类名查找元素

1
2
var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);

该方法可以通过 document 对象调用,也可以通过一个具体的元素对象调用。它返回一个 HTML 集合(类似数组的对象),包含了所有指定 class 名称的子元素。调用这个方法的元素将作为本次查找的根元素。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
</head>
<body>	
	<div class="test">类名为test的div</div>
	<div id="first">
		<div>内嵌div</div>
		<span class="test">类名为test的span</span>
	</div>
	<script type="text/javascript">
		var elements = document.getElementsByClassName("test");
		console.log(elements);
		var _firstDiv = document.getElementById("first");
		elements = _firstDiv.getElementsByClassName("test");
		console.log(elements);
	</script>
</body>
</html>

需要注意的是,该方法存在兼容性问题,在 IE 9 之前的版本中该方法不适用。我们可以这样来解决兼容的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getByClassName(className, rootElement) {
	rootElement = rootElement || document;
	if (rootElement.getElementsByClassName)
		return rootElement.getElementsByClassName(className);

	var tags = rootElement.getElementsByTagName("*"),	
		result = [];
	for (var i = 0, len = tags.length; i < len; i++) {
		var classNames = tags[i].className.split(" ");
		for (var j = 0, length = classNames.length; j < length; j++) {
			if (classNames[j] === className){
				result.push(tags[i]);
				break;
			}
		}
	}

	return result;
}

tags[i].className.split(" ") 中的 className 为 DOM 元素对象的属性,指代的是 HTML 元素节点中的 class(CSS类)属性。如果一个元素使用了多个类名,通常我们使用空格(“ “)将类名分隔开作为元素的 class 属性值。

3.2 添加元素

创建元素节点

1
var element = document.createElement(tagName);

tagName 指定将要创建的元素类型的字符串(标签名),该方法返回创建的 HTML 元素。

设置属性

1
element.setAttribute(name, value);

该方法用于添加一个新属性(attribute)到元素上,或改变元素上已经存在的属性的值。name 是表示属性名称的字符串,value 是属性的新值。

设置元素内 HTML 文本内容

1
element.innerHTML = markup;

markup 是包含元素(以及子元素)内容的字符串。元素的内容以原始 HTML 代码显示出来。

插入节点

1
var child = parentNode.appendChild(child);

该方法将一个节点插入到指定的父节点的最末尾处(也就是成为了这个父节点的最后一个子节点)。child 既是参数又是这个方法的返回值,即 appendChild() 方法会把要插入的这个节点引用作为返回值返回。

1
var insertedElement = parentNode.insertBefore(newElement, referenceElement);

该方法在父节点的某个子节点之前再插入一个子节点。如果 referenceElement 为 null 则 newElement 将被插入到子节点的末尾,如果 newElement 已经在 DOM 树中,newElement 首先会从 DOM 树中移除,再插入指定的 referenceElement 元素前。

注意:没有 insertAfter() 方法。可以使用 insertBefore() 方法和 nextSibling 来模拟这个操作。

综上,示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
	<style type="text/css">
		.test {width:200px; height:100px; background:red;}
	</style>
</head>
<body>
	<script type="text/javascript">
		var _div = document.createElement("div");
		_div.setAttribute("class", "test");
		_div.innerHTML = "<a href='http://www.baidu.com'>百度</a>";
		document.getElementsByTagName("body")[0].appendChild(_div);
	</script>
</body>
</html>

3.3 删除元素

1
var oldChild = parentNode.removeChild(child);

该方法从某个父节点中移除指定的子节点,并返回那个子节点。

child 是要移除的那个子节点,parentNode 是 child 的父节点,oldChild 和 child 指向同一个节点,即 oldChild === child。

被移除的这个子节点仍然存在于内存中,只是没有添加到当前文档的 DOM 树中,因此,我们还可以把这个节点重新添加回文档中。当然,实现要用另外一个变量,比如上例中的 oldChild 来保存这个节点的引用。

如果 child 节点不是 parentNode 节点的子节点,则该方法会抛出异常。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
	<style type="text/css">
		.test {width:200px; height:100px; background:red;}
	</style>
</head>
<body>
	<button id="del">删除</button>
	<div class="test">测试 div 元素节点</div>
	<script type="text/javascript">
		document.getElementById("del").onclick = function(){
			var _div = document.getElementsByClassName("test")[0],
				_body = document.getElementsByTagName("body")[0];
			_body.removeChild(_div);
			alert("删除成功...")
		};
	</script>
</body>
</html>

3.4 替换节点

1
var replacedNode = parentNode.replaceChild(newChild, oldChild);

该方法用指定的节点替换父节点的一个子节点,并返回被替换掉的节点。

newChild 是用来替换 oldChild 的新节点,如果该节点已经存在于DOM树中,则它会被从原始位置删除。oldChild 是待替换掉的原始节点,replacedNode 和 oldChild 相等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
	<style type="text/css">
		.test {width:200px; height:100px; background:red;}
	</style>
</head>
<body>
	<button id="replace">替换</button>
	<div class="test">测试 div 元素节点</div>
	<script type="text/javascript">
		document.getElementById("replace").onclick = function(){
			var _div = document.getElementsByClassName("test")[0],
				_body = document.getElementsByTagName("body")[0],
				_newNode = document.createElement("span");
			_newNode.innerHTML = "被替换后的内容";
			_body.replaceChild(_newNode, _div);
			alert("替换成功...")
		};
	</script>
</body>
</html>

3.5 克隆节点

1
var dupNode = node.cloneNode(deep);

该方法返回调用的节点的一个副本,node 为将要被克隆的节点,dupNode 是克隆生成的副本节点。deep 是可选参数,表示是否采用深度克隆,如果为 true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
	<style type="text/css">
		.test {width:200px; height:100px; background:red; margin-top: 5px;}
	</style>
</head>
<body>
	<button id="clone">克隆</button>
	<div class="test">测试 div 元素节点
		<span>内嵌 span 元素</span>
	</div>
	<script type="text/javascript">
		document.getElementById("clone").onclick = function(){
			var _div = document.getElementsByClassName("test")[0],
				_body = document.getElementsByTagName("body")[0],
				_newNode = _div.cloneNode(true);
			_body.appendChild(_newNode);
		};
	</script>
</body>
</html>

4. HTML DOM 属性

常用的 HTML DOM 属性有:

属性 描述
childNodes 返回父节点下所有子节点的集合(包括空白文本节点)
firstChild 返回父节点下第一个子节点
innerText 返回父节点内部纯文本(非标准属性)
lastChild 返回父节点下最后一个子节点
nextSibling 返回指定节点的后一个兄弟节点
nodeName 返回节点名称
nodeType 返回节点类型(1元素 2属性 3文本)
nodeValue 返回节点值
parentNode 返回指定节点的父节点
previousSibling 返回指定节点的前一个兄弟节点
innerHTML 返回指定节点内部 HTML 文本
className 设置/获取元素节点的CSS类名

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
</head>
<body>
	<div id="div-01">div-01</div>
	<div id="div-02">div-02</div>

	<script type="text/javascript">
		var el = document.getElementById('div-01').nextSibling,
			i = 1;
		console.log('Siblings of div-01:');
		while (el) {
			console.log(i + '. ' + el.nodeName + ", nodeType = " + el.nodeType);
			el = el.nextSibling;
			i++;
		}
	</script>
</body>
</html>

执行结果:

1
2
3
4
5
Siblings of div-01:
1. #text, nodeType = 3
2. DIV, nodeType = 1
3. #text, nodeType = 3
4. SCRIPT, nodeType = 1

5. CSS 样式

HTMLElement.style 属性返回一个 CSSStyleDeclaration 对象,表示元素的 内联 style 属性(attribute)。

由于 DOM 中 style 属性的优先级和通过 HTML 元素的 style 属性设置内联样式是一样的,并且在 css 层级样式中拥有最高优先级,因此在为特定的元素设置样式时很有用。

5.1 设置 style 属性

注意不能通过直接给 style 属性设置字符串(如:elt.style = "color: blue;")来设置 style,因为 style 应被当成是只读的(尽管 Firefox(Gecko), Chrome 和 Opera 允许修改它),这是因为通过 style 属性返回的 CSSStyleDeclaration 对象是只读的。但是 style 属性本身的属性能够用来设置样式。此外,通过单独的样式属性(如:elt.style.color = '...')比用 elt.style.cssText = '...' 或者 elt.setAttribute('style', '...') 形式更加简便,除非你希望完全通过一个单独语句来设置元素的全部样式,因为通过 style 本身属性设置的样式不会影响到通过其他方式设置的其他 css 属性的样式。

示例:

1
2
3
4
elt.style.cssText = "color: blue"; // 设置多个样式属性 
elt.setAttribute("style", "color: blue"); // 设置多个样式属性 
elt.style.color = "blue"; // 直接设置样式属性
var st = elt.style; st.color = "blue"; // 间接设置样式属性

如果 css 属性名称是使用 -(减号)连接的(如:font-size),而在 JavaScript 中 -(减号)表示算术运算,那么需要将 -(减号)去掉,将 -(减号)后的单词第一个字母改为大写状态。如:

1
2
elt.style.fontSize = "20px"; // 设置字体大小为 20px
elt.style.backgroundColor = "#ccc"; // 设置背景颜色

css 属性名书写方式可参见 CSS Properties Reference 中 JavaScript 的写法。

5.2 获取元素样式信息

通常,要了解元素样式的信息,仅仅使用 style 属性是不够的,这是因为它只包含了在元素内嵌 style 属性(attribute)上声明的 CSS 属性,而不包括来自其他地方声明的样式,如 <head> 部分的内嵌样式表,或外部样式表。要获取一个元素的所有 CSS 属性,可以使用 window.getComputedStyle()。

1
var style = window.getComputedStyle(element, [pseudoElt]);

参数 element 表示用于计算样式的元素,而 pseudoElt 为可选参数,指定一个伪元素进行匹配。对于常规的元素来说 pseudoElt 可省略。

getComputedStyle() 方法在 IE9 之前的版本中不支持,对于 IE 老版本来说,我们需要使用 element.currentStyle 来获取样式信息。

为解决老版本 IE 浏览器获取样式的兼容性问题,我们可以做如下封装:

1
2
3
4
function css(element, attr) {
	return element.currentStyle ? element.currentStyle[attr] : 
			getComputedStyle(element)[attr];
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试</title>
	<style type="text/css">
		.test {width:300px; height:100px; background:#ccc; color:#00f;}
	</style>
</head>
<body>
	<div id="div-01" class="test">div-01</div>

	<script type="text/javascript">
		var element = document.getElementById("div-01");
		var _width = css(element, "width");
		console.log("宽度:" + _width);

		function css(element, attr) {
			return element.currentStyle ? element.currentStyle[attr] : 
					getComputedStyle(element)[attr];
		}
	</script>
</body>
</html>

您的支持是我的动力。您可以通过以下方式支持我:

  • 关注我的 Github  关注我的 Github
  • 打赏
    微信 支付宝
    微信支付二维码 支付宝二维码
  • 关注我的微信公众号
    微信公众号
  • 文章标题: JavaScript 中的 HTML DOM 操作
  • 文章字数: 819
  • 本文作者: isaac
  • 发布时间: 2016年12月06日
  • 最后更新: 2016年12月06日
  • 原始链接: http://itrain.top/2016/12/javascript_dom/

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 转载请注明出处!