JavaScript DOM

本文最后更新于:8 个月前

长文预警,本文内容包括:DOM概述、Node接口、Element接口、以及常见的document节点、Element节点、Text节点,和节点属性、css的操作

JavaScript DOM

参考声明:部分内容整理自DOM - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack,需要了解更多细节,请阅读原文

一、DOM 概述

1、DOM

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。

浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。

DOM 只是一个接口规范,可以用各种语言实现。

2、节点

DOM 的最小组成单位叫做节点(node)

节点的类型有七种。

  • Document:整个文档树的顶层节点
  • DocumentTypedoctype标签(比如<!DOCTYPE html>
  • Element:元素节点,网页的各种HTML标签(比如<body><a>等)
  • Attr:网页元素的属性(比如class="right"
  • Text:文本节点,标签之间或标签包含的文本
  • Comment:注释节点
  • DocumentFragment:文档的片段,一个存在于内容的DOM片段,常用来生成复杂DOM结构,然后再插入当前文档

浏览器提供一个原生的节点对象 Node,上面这七种节点都继承了 Node,因此具有一些共同的属性和方法。

3、节点树

DOM HTML 树

浏览器原生提供document节点,代表整个文档。

文档的第一层有两个节点,第一个是文档类型节点(<!doctype html>)通过document.doctype获取,第二个是 HTML 网页的顶层容器标签<html>通过document.documentElement获取。后者构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。

除了根节点,其他节点都有三种层级关系。

  • 父节点关系(parentNode):直接的那个上级节点
  • 子节点关系(childNodes):直接的下级节点
  • 同级节点关系(sibling):拥有同一个父节点的节点

二、Node 接口

所有 DOM 节点对象都继承了 Node 接口(所有DOM继承了EventTarget接口),拥有一些共同的属性和方法。这是 DOM 操作的基础。

1、属性

nodeType

返回一个整数值,表示节点的类型

节点 类型 例子
ELEMENT_NODE 元素节点 1 <h1 class="heading">W3School</h1>
ATTRIBUTE_NODE 属性节点 2 class = "heading" (弃用)
TEXT_NODE 文本节点 3 W3School
COMMENT_NODE 注释节点 8 <!-- 这是注释 -->
DOCUMENT_NODE 文档节点 9 HTML 文档本身(<html> 的父)
DOCUMENT_TYPE_NODE 文档类型节点 10 <!Doctype html>

nodeName

返回节点的名称

不同节点的nodeName属性值如下。

  • 文档节点(document):#document
  • 元素节点(element):大写的标签名
  • 属性节点(attr):属性的名称
  • 文本节点(text):#text
  • 文档片断节点(DocumentFragment):#document-fragment
  • 文档类型节点(DocumentType):文档的类型
  • 注释节点(Comment):#comment

nodeValue

返回一个字符串,表示当前节点本身的文本值,该属性可读写

只有文本节点(text)、注释节点(comment)和属性节点(attr)有文本值,因此这三类节点的nodeValue可以返回结果,其他类型的节点一律返回null

1
2
3
4
5
// HTML 代码如下
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeValue // null
div.firstChild.nodeValue // "hello world"

textContent

返回当前节点和它的所有后代节点的文本内容。自动忽略当前节点内部的 HTML 标签,返回所有文本内容。

1
2
3
4
// HTML 代码为
// <div id="divA">This is <span>some</span> text</div>
document.getElementById('divA').textContent
// This is some text

该属性是可读写的,设置该属性的值,会用一个新的文本节点,覆盖掉所有原来的子节点,而且自动对HTML标签转义(即不会当做标签处理)

对于文本节点(text)、注释节点(comment)和属性节点(attr),textContent属性的值与nodeValue属性相同

对于其他类型的节点,该属性会将每个子节点(不包括注释节点)的内容连接在一起返回。如果一个节点没有子节点,则返回空字符串。

如果要读取整个文档的内容,可以使用document.documentElement.textContent

父子兄弟节点属性

属性 说明
nextSibling 返回紧跟在当前节点后面的第一个同级节点,如果没有返回 null
该属性还包括文本节点和注释节,因此如果当前节点后面有空格,该属性会返回一个文本节点,内容为空格,例一
previousSibling 返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回null
parentNode 返回当前节点的父节点
对于一个节点来说,它的父节点只可能是三种类型:元素节点、文档节点、文档片段节点
parentElement 返回当前节点的父元素节点。如果当前节点没有父节点,或者父节点类型不是元素节点,则返回null
firstChild 返回当前节点的第一个子节点,如果当前节点没有子节点,则返回null
返回的节点除了元素节点,还可能是文本节点或注释节点,例二
lastChild 返回当前节点的最后一个子节点,如果当前节点没有子节点,则返回null
childNodes 返回一个类似数组的对象(NodeList集合),成员包括当前节点的所有子节点,包括元素节点、文本节点、注释节点
NodeList对象是一个动态集合,一旦子节点发生变化,立刻会反映在返回结果之中。

例一

1
2
3
4
5
//<div class="box3">
// <p>文本1</p>
// <p>文本2</p>
//</div>
document.querySelector('.box3 p').nextSibling.nodeValue //'\n'

例二

1
2
3
4
5
6
// HTML 代码如下
// <p id="p1">
// <span>First span</span>
// </p>
var p1 = document.getElementById('p1');
p1.firstChild.nodeName // "#text"

2、方法

appendChild()

接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点。

如果参数节点是 DOM 已经存在的节点,appendChild()方法会将其从原来的位置,移动到新位置

1
2
3
var p = document.createElement('p');
var p2 = document.body.appendChild(p);
p === p2 //true

如果appendChild()方法的参数是DocumentFragment节点,那么插入的是DocumentFragment的所有子节点,而不是DocumentFragment节点本身。返回值是一个空的DocumentFragment节点。一次性插入,减少DOM操作

insertBefore()

用于将某个节点插入父节点内部的指定位置。接受两个参数,第一个参数是所要插入的节点newNode,第二个参数是父节点parentNode内部的一个子节点referenceNodenewNode将插在referenceNode这个子节点的前面。返回值是插入的新节点newNode

如果insertBefore方法的第二个参数为null,则新节点将插在当前节点内部的最后位置,即变成最后一个子节点。

如果所要插入的节点是当前 DOM 现有的节点,则该节点将从原有的位置移除,插入新的位置。

如果要插入的节点是DocumentFragment类型,那么插入的将是DocumentFragment的所有子节点,而不是DocumentFragment节点本身。返回值将是一个空的DocumentFragment节点。

removeChild()

接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点

被移除的节点依然存在于内存之中,但不再是 DOM 的一部分。所以,一个节点移除以后,依然可以使用它,比如插入到另一个节点下面。

其它方法

方法 说明
hasChildNodes() 返回一个布尔值,表示当前节点是否有子节点。
注意,子节点包括所有类型的节点,并不仅仅是元素节点
cloneNode() 用于克隆一个节点。它接受一个布尔值作为参数,表示是否同时克隆子节点。返回值是一个克隆的新节点
注意:1.克隆一个节点,会拷贝该节点的所有属性,但是会丧失addEventListener方法和on-属性
2.,该方法返回的节点不在文档之中,即没有任何父节点,必须使用诸如Node.appendChild这样的方法添加到文档之中
3.克隆一个节点之后,DOM 有可能出现两个有相同id属性,这时应该修改其中一个元素的id属性
replaceChild() 用于将一个新的节点,替换当前节点的某一个子节点
接受两个参数,第一个参数newChild是用来替换的新节点,第二个参数oldChild是将要替换走的子节点。返回值是替换走的那个节点oldChild
contains() 返回一个布尔值,表示参数节点是否是当前节点的本身、或子节点、或后代节点
normalize() 用于清理当前节点内部的所有文本节点(text)
它会去除空的文本节点,并且将毗邻的文本节点合并成一个,也就是说不存在空的文本节点,以及毗邻的文本节点
getRootNode() 返回当前节点所在文档的根节点document,与ownerDocument属性的作用相同
1
2
3
4
5
6
var wrapper = document.createElement('div');
wrapper.appendChild(document.createTextNode('Part 1 '));
wrapper.appendChild(document.createTextNode('Part 2 '));
wrapper.childNodes.length // 2
wrapper.normalize();
wrapper.childNodes.length // 1

三、NodeList 和 HTMLCollection

节点都是单个对象,有时需要一种数据结构,能够容纳多个节点。DOM 提供两种节点集合,用于容纳多个节点:NodeListHTMLCollection。两者主要区别是,NodeList可以包含各种类型的节点HTMLCollection只能包含 HTML 元素节点

共同点:

  • 类数组对象,能够通过下标直接访问,可以使用 length属性,可以遍历,但是不具备其它数组方法

1、NodeList

可以使用length属性和forEach方法。但是,它不是数组,不能使用poppush之类数组特有的方法

通过以下方法可以得到NodeList实例。

  • Node.childNodes
  • document.querySelectorAll()等节点搜索方法

注意,NodeList 实例可能是动态集合,也可能是静态集合。目前,只有Node.childNodes返回的是一个动态集合,其他的 NodeList 都是静态集合。

1
2
3
4
var children = document.body.childNodes;
children.length // 18
document.body.appendChild(document.createElement('p'));
children.length // 19

keys、values、entries方法

1
2
3
4
5
6
7
8
9
10
11
12
var children = document.body.childNodes;
for (var key of children.keys()) {
console.log(key);
}

for (var value of children.values()) {
console.log(value);
}

for (var entry of children.entries()) {
console.log(entry);
}

2、HTMLCollection

节点对象的集合,只能包含元素节点(element),不能包含其他类型的节点,不能使用 forEach方法,只能用 for循环遍历

返回HTMLCollection实例的,主要是一些Document对象的集合属性,比如document.linksdocument.formsdocument.images等。

HTMLCollection实例都是动态集合,节点的变化会实时反映在集合中。

如果元素节点有idname属性,那么HTMLCollection实例上面,可以使用id属性或name属性引用该节点元素。如果没有对应的节点,则返回null

1
2
3
4
// HTML 代码如下
// <img id="pic" src="http://example.com/foo.jpg">
var pic = document.getElementById('pic');
document.images.pic === pic // true

HTMLCollection.prototype.namedItem()

参数是一个字符串,表示id属性或name属性的值,返回对应的元素节点

1
2
3
4
// HTML 代码如下
// <img id="pic" src="http://example.com/foo.jpg">
var pic = document.getElementById('pic');
document.images.namedItem('pic') === pic // true

四、ParentNode 和 ChildNode

节点对象除了继承 Node 接口以外,还拥有其他接口。ParentNode接口表示当前节点是一个父节点,提供一些处理子节点的方法。ChildNode接口表示当前节点是一个子节点,提供一些相关方法

感觉这两个node都是HTMLElement,和HTMLCollection的子元素有相同的属性和接口

1、ParentNode 接口

如果当前节点是父节点,就会混入了(mixin)ParentNode接口。由于只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点,因此只有这三类节点会拥有ParentNode接口

属性/方法 说明
children 返回一个HTMLCollection实例,成员是当前节点的所有元素子节点,不包含其它类型子节点。该属性只读。是动态集合
firstElementChild 当前节点的第一个元素子节点,若没有则返回null
lastElementChild 当前节点的最后一个元素子节点,若没有则返回null
childElementCount 所有元素子节点的数目
append() 为当前节点追加一个或多个子节点,位置是最后一个元素子节点的后面。
不仅可以填加元素子节点,还可以添加文本子节点,该方法没有返回值
prepend() 为当前节点追加一个或多个子节点,位置是第一个元素子节点的前面,没有返回值
1
2
3
4
5
6
// 添加文本子节点
parent.append('Hello');
// 添加多个元素子节点
var p1 = document.createElement('p');
var p2 = document.createElement('p');
parent.append(p1, p2);

2、ChildNode 接口

如果一个节点有父节点,那么该节点就拥有了ChildNode接口

属性/方法 说明
remove() 用于从父节点移除当前节点
before() 用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点,不仅可以插入元素,还可以插入文本节点
after() 用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点
replaceWith() 使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点
1
2
3
4
5
6
7
8
9
10
el.remove()		//在DOM里面删除了el

var p = document.createElement('p');
// 插入元素节点
el.before(p);
// 插入文本节点
el.before('Hello');

var span = document.createElement('span');
el.replaceWith(span); //el节点将被span节点替换

五、Document 节点

document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。

document的原型继承如下:

1
2
3
4
5
6
7
document实例对象
HTMLDocument
Document
Node
EventTarget
Object
null

document对象有不同的办法可以获取。

  • 正常的网页,直接使用documentwindow.document
  • iframe框架里面的网页,使用iframe节点的contentDocument属性。
  • Ajax 操作返回的文档,使用XMLHttpRequest对象的responseXML属性。
  • 内部节点的ownerDocument属性。

document对象继承了EventTarget接口和Node接口,并且混入(mixin)了ParentNode接口。这意味着,这些接口的方法都可以在document对象上调用。除此之外,document对象还有很多自己的属性和方法

1、属性

下表列举部分常见的属性

属性 说明
document.doctype 指向<DOCTYPE>节点,即文档类型节点
documentElement 返回当前文档的根元素节点(root),一般是<html>节点
activeElement 返回获得当前焦点(focus)的 DOM 元素,如果当前没有焦点元素,返回<body>元素或null
节点集合属性 说明
document.links 返回当前文档所有设定了href属性的<a><area>节点
forms 返回所有<form>表单节点
images 返回页面所有<img>图片节点
scripts 返回所有<script>节点
styleSheets 返回文档内嵌或引入的样式表集合
文档静态信息属性 说明
document.documentURI 返回一个字符串,表示当前文档的网址,继承自Document接口,可用于所有文档
document.URL 返回一个字符串,表示当前文档的网址,继承自HTMLDocument接口,只能用于 HTML 文档
如果文档的锚点(#anchor)变化,这两个属性都会跟着变化
domain 返回当前文档的域名,不包含协议和端口
比如:http://www.example.com:80/hello.html,返回:www.example.com。补充一
lastModified 返回一个字符串,表示当前文档最后修改的时间,需要转换为Date类型后才能相互比较
characterSet 返回当前文档的编码,比如UTF-8ISO-8859-1
referrer 返回一个url字符串,表示当前文档的访问者来自哪里。如果无法获取或者没有来源,返回空字符串
  • 补充一document.domain基本上是一个只读属性,只有一种情况除外。次级域名的网页,可以把document.domain设为对应的上级域名。比如,当前域名是a.sub.example.com,则document.domain属性可以设置为sub.example.com,也可以设为example.com。修改后,document.domain相同的两个网页,可以读取对方的资源,比如设置的 Cookie。

    设置document.domain会导致端口被改成null。因此,如果通过设置document.domain来进行通信,双方网页都必须设置这个值,才能保证端口相同。

文档状态属性 说明
document.hidden 返回一个布尔值,表示当前页面是否可见。如果窗口最小化、浏览器切换了 Tab,都会导致导致页面不可见,使得document.hidden返回true
visibilityState 返回文档的可见状态。范围为4个值:visible(页面可见)、hidden(页面不可见)、prerender(页面处于正在渲染状态)、unloaded(页面从内存里面卸载了)
readyState 返回当前文档的状态。范围为3个值:loading:加载 HTML 代码阶段(尚未完成解析)、
interactive:加载外部资源阶段、complete:加载完成
每次状态变化都会触发一个readystatechange事件
designMode 控制当前文档是否可编辑。该属性只有两个值onoff,默认值为off。一旦设为on,用户就可以编辑整个文档的内容

2、方法

获取DOM节点的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
document.getElementsByTagName('p');	//传入字符串类型,标签名,大小写不敏感;返回值是HTMLCollection对象,若无匹配返回空集
//如果传入*,返回所有HTML元素
//该方法不仅可以在document对象上调用,也可以在任何元素节点上调用

document.getElementsByClassName('foo bar'); //传入类名,可以多个(空格隔开);返回HTMLCollection对象
//以上代码返回同时具有foo和bar类的元素
//该方法可以在任何元素节点上调用

document.getElementsByName('x') //用于选择有name属性的HTML元素,如form、radio、img、frame、embed;返回NodeList对象

document.getElementById('para1'); //传入id,大小写敏感;返回节点,若无匹配返回null
//document.getElementById()比document.querySelector()效率高得多
//该方法只能在document对象上使用,不能在其他元素节点上使用
1
2
document.querySelector();	//传入css选择器规则,如“#yourId"、".yourClass"等,返回第一个匹配到的元素,若无匹配返回null
document.querySelectorAll();//参数同上,返回一个NodeList对象,包含所有匹配的节点。如果传入*,返回所有节点

以上两种方法不支持 CSS 伪元素和伪类的选择器。这两个方法除了定义在document对象上,还定义在元素节点上,即在元素节点上也可以调用

拓展:CSS选择器的组合方法

  • 多个选择器通过逗号,相连,表示同时选中各个选择器

    1
    h1,h2,p { }	表示同时选中h1,h2p
  • 多个类名之间没有间隔,连接在一起,表示同时具有这些类的所有元素

    1
    .class1.class2  表示选择同时具有class1和class2类的所有元素
  • 多个选择器之间用空格隔开,表示从前到后的深入关系

    1
    .class1 .class2 p  表示选择class1类元素里面的 class2类元素里面的 所有p标签

创建DOM节点的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
document.createElement('div')	//参数为元素标签名,即元素节点的tagName属性,大小写不敏感

document.createTextNode('Hello')//生成文本节点,参数是文本节点的内容,返回生成的文本节点
//该方法可以确保参数被当做文本渲染,而不是当做HTML代码被渲染,避免XSS攻击

document.createAttribute('my_attrib') //生成属性节点,传入参数为属性节点名,返回生成的节点
var a = document.createAttribute('my_attrib');
a.value = 'newVal'
var node = document.getElementById('div1')
node.setAttributeNode(a)
//以上操作也可以用下一行代码替代实现
node.setAttribute('my_attrib', 'newVal')

document.createComment('一段注释') //参数注释内容,返回新的注释节点

document.createDocumentFragment() //返回一个空的文档片段对象,是一个存在于内容的DOM片段,
//常用来生成复杂DOM结构,然后再插入当前文档
var docfrag = document.createDocumentFragment();
[1, 2, 3, 4].forEach(function (e) {
var li = document.createElement('li');
li.textContent = e;
docfrag.appendChild(li);
});
var element = document.getElementById('ul');
element.appendChild(docfrag);

六、Element 节点

Element节点对象对应网页的 HTML 元素。每一个 HTML 元素,在 DOM 树上都会转化成一个Element节点对象。

Element对象继承了Node接口,因此Node的属性和方法在Element对象都存在。

此外,不同的 HTML 元素对应的元素节点是不一样的,浏览器使用不同的构造函数,生成不同的元素节点,比如<a>元素的构造函数是HTMLAnchorElement()<button>HTMLButtonElement()。因此,元素节点不是一种对象,而是许多种对象,这些对象除了继承Element对象的属性和方法,还有各自独有的属性和方法。

Element元素节点的原型继承关系如下:

1
2
3
4
5
6
7
元素节点实例
HTMLElement
Element
Node
EventTarget
Object
null

1、实例属性

元素节点的属性

特性相关的属性

属性 说明
Element.id 指定元素的id属性
tagName 指定元素的大写标签名,与nodeName属性的值相等
dir 读写当前元素的文字方向
accessKey 用于读写分配给当前元素的快捷键
tabIndex 当前元素在 Tab 键遍历时的顺序。该属性可读写
title 用来读写当前元素的 HTML 属性title,通常用来指定,鼠标悬浮时弹出的文字提示框
1
2
3
// <button accesskey="h" id="btn">点击</button>
var btn = document.getElementById('btn');
btn.accessKey // "h",btn元素的快捷键是h

状态相关的属性

属性 说明
hidden 一个布尔值,表示当前元素的hidden属性,用来控制当前元素是否可见,可读写
与 CSS 设置是互相独立的,CSS 的设置高于Element.hidden
contentEditable 字符串(’true’、’false’、’inherit’),元素的内容是否可以编辑,可读写
isContentEditable 同上,只读
attributes 类数组对象,包含当前元素节点所有的属性节点
className 字符串,读写当前元素节点的class属性,每个class之间用空格分割
classList 类数组对象,包含所有类名。具有多种方法:add、remove、contains、toString、toggle
dataset 网页元素可以自定义data-属性,用来添加数据,Element.dataset属性返回一个对象,可以从这个对象读写data-属性
innerHTML 返回一个字符串,等同于该元素包含的所有 HTML 代码,可读写。
返回实体符号(如<)时,innerHTML会将它转为实体形式(&amp
注意:如果赋值的文本中包含<script>标签,虽然可以生成script节点,但是不会执行
outerHTML 返回一个字符串,表示当前元素节点的所有 HTML 代码,包括该元素本身和所有子元素,可读写
注意,如果一个节点没有父节点,设置outerHTML属性会报错
Element.clientHeight 整数值,表示元素节点的 CSS 高度(单位像素),只对块级元素生效,对于行内元素返回0
如果块级元素没有设置 CSS 高度,则返回实际高度
包括padding,不包括border和margin,去除水平滚动条高度
clientWidth 返回元素节点的 CSS 宽度,同样只对块级元素有效,去除滚动条宽度
clientLeft 元素左边框的宽度,行内元素为0
scrollHeight 当前元素的总高度(px),包括溢出部分
scrollWidth 当前元素的总宽度(px)
scrollLeft 当前元素的水平滚动条向右侧滚动的像素数量
scrollTop 当前元素的垂直滚动条向下滚动的像素数量
style 用来读写该元素的行内样式信息
children HTMLCollection实例,包括当前元素节点的所有子元素,只包含元素节点,不包含其它类型节点
childElementCount 当前元素节点包含的子元素节点的个数
firstElementChild 当前元素的第一个元素子节点
lastElementChild 最后一个元素子节点
nextElementSibling 前一个同级元素节点
previousElementSibling 后一个同级元素节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
div.className	// "one two three"
div.className += 'bold';

div.classList.add('myCssClass');
div.classList.add('foo', 'bar');
div.classList.remove('myCssClass');
div.classList.toggle('myCssClass'); // 如果 myCssClass 不存在就加入,否则移除
div.classList.contains('myCssClass'); // 返回 true 或者 false
div.classList.item(0); // 返回第一个 Class
div.classList.toString();//将 class 的列表转为字符串。

//<div data-timestamp="1522907809292"></div>
document.querySelector('div').dataset.timestamp
//data-a-def 对应 dataset.aDef data-a-1 对应 dataset['a-1']

document.documentElement.clientHeight //浏览器窗口的高度
window.innerHeight //同上
document.body.clientHeight //网页当前视口的实际高度
document.body.scrollHeight //网页的总高度,包括不可见部分

2、实例方法

元素节点的方法

属性相关

元素节点提供六个方法,用来操作属性。

  • getAttribute():读取某个属性的值
  • getAttributeNames():返回当前元素的所有属性名
  • setAttribute():写入属性值
  • hasAttribute():某个属性是否存在
  • hasAttributes():当前元素是否有属性
  • removeAttribute():删除属性

获取节点相关

  • querySelector(),接受css选择器作为参数,返回第一个匹配的子元素,无法选中伪元素和伪类
  • querySelectorAll(),接受css选择器作为参数,返回所有匹配的子元素(NodeList对象),若参数为*,返回所有子元素
  • getElementsByTagName()
  • getElementsByClassName()
  • closest(),接受一个 CSS 选择器作为参数,返回匹配该选择器的、最接近当前节点的一个祖先节点(包括当前节点本身)
  • matches(),返回一个布尔值,表示当前元素是否匹配给定的 CSS 选择器

事件相关

  • addEventListener()
  • removeEventListener()
  • dispatchEvent()

scrollIntoView()

滚动当前元素,进入浏览器的可见区域,类似于设置window.location.hash的效果

1
2
el.scrollIntoView(); 		//参数默认为true,元素的顶部与当前可见部分的顶部对齐
el.scrollIntoView(false); //元素的底部与当前可见部分的底部对齐

getBoundingClientRect()

返回一个对象,提供当前元素节点的大小、位置等信息,基本上就是 CSS 盒状模型的所有信息,返回对象的参数如下;

  • x:元素左上角相对于视口的横坐标
  • y:元素左上角相对于视口的纵坐标
  • height:元素高度
  • width:元素宽度
  • top:元素顶部相对于视口的纵坐标,与y属性相等
  • bottom:元素底部相对于视口的纵坐标(等于y + height

remove()

继承自 ChildNode 接口,用于将当前元素节点从它的父节点移除

focus() 和 blur()

用于将当前页面的焦点,转移到指定元素上。

1
2
document.getElementById('btn').focus({preventScroll:false});	//btn获得焦点,并滚动到可见区域
document.getElementById('btn').focus({preventScroll:true}); //btn获得焦点,但停留在原始位置

七、属性的操作

HTML 元素包括标签名和若干个键值对,这个键值对就称为“属性”(attribute)

1
<a id="test" href="http://www.example.com">链接</a>

HTML 元素的属性名是大小写不敏感的,但是 JavaScript 对象的属性名是大小写敏感的。JavaScript对象的属性名采取驼峰格式,如onClick

Element.attributes

类数组对象,包含当前元素标签的所有属性节点对象

1
2
3
4
5
6
7
8
9
10
11
12
13
//<div class="container" id="top">
// <p class="para"></p>
//</div>
var container = document.getElementById('top')
container.attributes //类数组对象,只包含当前div标签的属性
container.attributes[0] //class="container"
container.attributes.class //class="container"
container.attributes['class'] //class="container"

container.attributes[0].name //'class'
container.attributes[0].nodeName //'class'
container.attributes[0].value //'container'
container.attributes[0].nodeValue //'container'

操作的方法

元素节点提供六个方法,用来操作属性。

  • getAttribute(),返回当前元素节点的指定属性。如果指定属性不存在,则返回null
  • getAttributeNames(),返回一个数组(而不是类数组),成员是当前元素的所有属性的名字
  • setAttribute(),用于为当前元素节点新增属性。如果同名属性已存在,则相当于编辑已存在的属性
  • hasAttribute(),返回一个布尔值,表示当前元素节点是否包含指定属性
  • hasAttributes(),返回一个布尔值,表示当前元素是否有属性
  • removeAttribute(),移除指定属性。该方法没有返回值

dataset属性

有时,需要在HTML元素上附加数据,解决方法是,使用标准提供的data-*属性

在HTML中,data-后面的属性名有限制,只能是:小写字母、短横线-、点.、冒号:、下划线_

与JavaScript对象的转换规则为:驼峰(JS对象)—— kebab-case(HTML),如:data-hello-world对应 dataset.helloWorld

删除一个data-*属性,可以直接使用delete命令

1
delete document.getElementById('myDiv').dataset.foo;

八、Text 节点

文本节点(Text)代表元素节点(Element)和属性节点(Attribute)的文本内容。

通常文本节点作为元素节点的第一个子节点

1
2
3
4
5
6
7
//获取text节点
//<div>
// <p>this is a text node</p>
//</div>
var p =document.querySelector('p')
p.firstChild //"this is a text node"
document.querySelector('div').childNodes //NodeList(3) [text, p, text]

文本节点除了继承Node接口,还继承了CharacterData接口。Node接口的属性和方法请参考《Node 接口》一章,这里不再重复介绍了,以下的属性和方法大部分来自CharacterData接2

属性 说明
data 等同于nodeValue属性,用来设置或读取文本节点的内容
wholeText 将当前文本节点与毗邻的文本节点,作为一个整体返回
length 返回当前文本节点的文本长度
nextElementSibling 返回紧跟在当前文本节点后面的那个同级元素节点
1
2
3
4
//<p id="para">A <em>B</em> C</p>
var el = document.getElementById('para')
el.firstChild.wholeText //'A '
el.firstChild.data //'A '

方法

  • appendData():在Text节点尾部追加字符串。
  • deleteData():删除Text节点内部的子字符串,第一个参数为子字符串开始位置,第二个参数为子字符串长度。
  • insertData():在Text节点插入字符串,第一个参数为插入位置,第二个参数为插入的子字符串。
  • replaceData():用于替换文本,第一个参数为替换开始位置,第二个参数为需要被替换掉的长度,第三个参数为新加入的字符串。
  • subStringData():用于获取子字符串,第一个参数为子字符串在Text节点中的开始位置,第二个参数为子字符串长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
// HTML 代码为
// <p>Hello World</p>
var pElementText = document.querySelector('p').firstChild;
pElementText.appendData('!');
// 页面显示 Hello World!
pElementText.deleteData(7, 5);
// 页面显示 Hello W
pElementText.insertData(7, 'Hello ');
// 页面显示 Hello WHello
pElementText.replaceData(7, 5, 'World');
// 页面显示 Hello WWorld
pElementText.substringData(7, 10);
// 页面显示不变,返回"World "

九、CSS操作

1、内联CSS操作

  • 使用元素节点的setAttribute()等方法,最简单
  • 使用元素节点的style属性(它部署了CSSStyleDeclaration接口),可以读写个别属性
1
2
3
4
5
6
7
8
9
10
11
12
//方式一
div.setAttribute(
'style',
'background-color:red;' + 'border:1px solid black;'
);

//方式二
var e = document.querySelector('div')
e.style.fontSize = '18px'
e.style.color = "black"
e.removeProperty('color') //删除color属性
e.cssText = '' //清空当前元素的所有内联样式

2、增删类名修改样式

通过修改元素节点的className或classList属性,来达到修改样式的目的

  • className,字符串,读写当前元素节点的class属性,每个class之间用空格分割
  • classList,类数组对象,包含所有类名。具有多种方法:add、remove、contains、toString、toggle
1
2
3
4
5
6
7
8
9
10
div.className	// "one two three"
div.className += 'bold';

div.classList.add('myCssClass');
div.classList.add('foo', 'bar');
div.classList.remove('myCssClass');
div.classList.toggle('myCssClass'); // 如果 myCssClass 不存在就加入,否则移除
div.classList.contains('myCssClass'); // 返回 true 或者 false
div.classList.item(0); // 返回第一个 Class
div.classList.toString();//将 class 的列表转为字符串。

3、window.getComputedStyle()

行内样式(inline style)具有最高的优先级,改变行内样式,通常会立即反映出来。但是,网页元素最终的样式是综合各种规则计算出来的。因此,如果想得到元素实际的样式,只读取行内样式是不够的。

window.getComputedStyle

  • 参数一:节点对象,要获取样式的目标对象
  • 参数二:(可选)伪元素(比如:before:after:first-line:first-letter等)
  • 返回值:CSS 规则叠加后的的最终规则,一个 CSSStyleDeclaration 实例

4、CSS样式表操作

StyleSheet接口代表网页的一张样式表,包括<link>元素加载的样式表和<style>元素内嵌的样式表

document对象的styleSheets属性,可以返回当前页面的所有StyleSheet实例(即所有样式表)。它是一个类似数组的对象

更多细节,请阅读原文:DOM - CSS 操作 - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack

十、DocumentFragment 节点

DocumentFragment节点代表一个文档的片段,本身就是一个完整的 DOM 树形结构。它没有父节点,但是可以插入任意数量的子节点。它不属于当前文档,操作DocumentFragment节点,要比直接操作 DOM 树快得多。

一般用于构建一个 DOM 结构,然后插入当前文档。

1
2
3
4
5
6
7
var docFrag = document.createDocumentFragment();
// 等同于
var docFrag = new DocumentFragment();
var li = document.createElement('li');
li.textContent = 'Hello World';
docFrag.appendChild(li);
document.querySelector('ul').appendChild(docFrag);

DocumentFragment节点本身不能被插入当前文档,是它的所有子节点插入当前文档,一旦DocumentFragment节点被添加进当前文档,它自身就变成了空节点。

DocumentFragment节点对象没有自己的属性和方法,全部继承自Node节点和ParentNode接口。也就是说,DocumentFragment节点比Node节点多出以下四个属性。

  • children:返回一个动态的HTMLCollection集合对象,包括当前DocumentFragment对象的所有子元素节点。
  • firstElementChild:返回当前DocumentFragment对象的第一个子元素节点,如果没有则返回null
  • lastElementChild:返回当前DocumentFragment对象的最后一个子元素节点,如果没有则返回null
  • childElementCount:返回当前DocumentFragment对象的所有子元素数量。

经过实践发现,DocumentFrame好像并不能处理所有类型的元素节点,在借助它往DOM的div标签插入一组img时,并没有成功!

十一、Mutation Observer API

Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

具体操作请阅读:DOM - Mutation Observer API - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack

目前还没有遇到此类需求,所以暂时只做简单了解

十二、innerText和textContent的区别

两个属性并非html5的规范属性,而是各个浏览器厂商自己实现的。

  1. innerText的值近似于浏览器的显示效果,textContent近似于于代码的显示效果
  2. 如果一个元素之间包含了script标签或者style标签,innerText是获取不到这两个元素之间的文本的,而textContent可以
  3. textContent会把空标签解析成换行(几个空标签就是几行),innerText只会把block元素类型的空标签解析换行,并且如果是多个的话仍看成是一个,而inline类型的原素则解析成空格
  4. innerText的设置会引发浏览器的回流操作,而textContent则不一定会

参考:innerText 和 textContent 的区别 - 简书 (jianshu.com)


JavaScript DOM
http://timegogo.top/2022/04/14/JavaScript/DOM/
作者
丘智聪
发布于
2022年4月14日
更新于
2023年7月16日
许可协议