
2025-02-18 日报 Day101

今日的鸡汤
成才之路不一定会创造人生奇迹,但一定要有奋发向上的轨迹;成才之路不一定非要跑得最快,但一定需要屡败屡战的韧性;成才之路不一定拥有鲜花掌声,但应进一寸有一寸的欢喜。
今日学习内容
1、JS 红皮书 P401-413 第十四章:DOM
今日笔记
1、文档对象模型(DOM,Document Object Model)是 HTML 和 XML 文档的编程接口。DOM 表示由多层节点构成的文档,通过它开发者可以添加、删除和修改页面的各个部分。
2、节点层级: 任何 HTML 或 XML 文档都可以用 DOM 表示为一个由节点构成的层级结构。节点分很多类型,每种类型对应着文档中不同的信息和(或)标记,也都有自己不同的特性、数据和方法,而且与其他类型有某种关系。这些关系构成了层级,让标记可以表示为一个以特定节点为根的树形结构。以下面的 HTML为例:
1 | <html> |
DOM 中总共有 12 种节点类型,这些类型都继承一种基本类型。
- Node类型: 每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值常量表示:
- Node.ELEMENT_NODE: 1
- Node.ATTRIBUTE_NODE: 2
- Node.TEXT_NODE: 3
- Node.CDATA_SECTION_NODE: 4
- Node.ENTITY_REFERENCE_NODE: 5
- Node.ENTITY_NODE: 6
- Node.PROCESSING_INSTRUCTION_NODE: 7
- Node.COMMENT_NODE: 8
- Node.DOCUMENT_NODE: 9
- Node.DOCUMENT_TYPE_NODE: 10
- Node.DOCUMENT_FRAGMENT_NODE: 11
- Node.NOTATION_NODE: 12
nodeName与nodeValue: nodeName 与 nodeValue 保存着有关节点的信息。这两个属性的值完全取决于节点类型。在使用这两个属性前,最好先检测节点类型,如下所示:节点关系: 文档中的所有节点都与其他节点有关系。这些关系可以形容为家族关系,相当于把文档树比作家谱。在 HTML 中,元素是元素的子元素,而元素则是元素的父元素。元素是元素的同胞元素,因为它们有共同的父元素。1
2
3if (someNode.nodeType == 1){
value = someNode.nodeName; // 会显示元素的标签名
}
每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组对象,用于存储可以按位置存取的有序节点。注意,NodeList 并不是 Array 的实例,但可以使用中括号访问它的值,而且它也有 length 属性。NodeList 对象独特的地方在于,它其实是一个对 DOM 结构的查询,因此 DOM 结构的变化会自动地在 NodeList 中反映出来。我们通常说 NodeList 是实时的活动对象,而不是第一次访问时所获得内容的快照。
每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。childNodes 中的所有节点都有同一个父元素,因此它们的 parentNode 属性都指向同一个节点。此外,childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点。而使用 previousSibling 和 nextSibling 可以在这个列表的节点间导航。这个列表中第一个节点的 previousSibling 属性是 null,最后一个节点的nextSibling 属性也是 null,如下所示:因为所有关系指针都是只读的,所以 DOM 又提供了一些操纵节点的方法。最常用的方法是appendChild(),用于在 childNodes 列表末尾添加节点。添加新节点会更新相关的关系指针,包括父节点和之前的最后一个子节点。appendChild()方法返回新添加的节点,如下所示:1
2
3
4
5if (someNode.nextSibling === null){
alert("Last node in the parent's childNodes list.");
} else if (someNode.previousSibling === null){
alert("First node in the parent's childNodes list.");
}所有节点类型还共享了两个方法。第一个是 cloneNode(),会返回与调用它的节点一模一样的节点。cloneNode()方法接收一个布尔值参数,表示是否深复制。在传入 true 参数时,会进行深复制,即复制节点及其整个子 DOM 树。如果传入 false,则只会复制调用该方法的节点。复制返回的节点属于文档所有,但尚未指定父节点,所以可称为孤儿节点(orphan)。可以通过 appendChild()、insertBefore()或 replaceChild()方法把孤儿节点添加到文档中。以下面的 HTML 片段为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19let returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); // true
alert(someNode.lastChild == newNode); // true
// 假设 someNode 有多个子节点
let returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); // false
alert(returnedNode == someNode.lastChild); // true
// 作为最后一个子节点插入
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); // true
// 作为新的第一个子节点插入
returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); // true
alert(newNode == someNode.firstChild); // true
// 插入最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true如果myList保存着对这个1
2
3
4
5<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>- 元素的引用,则下列代码展示了使用cloneNode()方法的两种方式:
本节要介绍的最后一个方法是 normalize()。这个方法唯一的任务就是处理文档子树中的文本节点。由于解析器实现的差异或 DOM 操作等原因,可能会出现并不包含文本的文本节点,或者文本节点之间互为同胞关系。在节点上调用 normalize()方法会检测这个节点的所有后代,从中搜索上述两种情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。1
2
3
4let deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); // 3(IE9 之前的版本)或 7(其他浏览器)
let shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); // 0
3、Document类型: 在浏览器中,文档对象 document 是HTMLDocument 的实例(HTMLDocument 继承 Document),表示整个 HTML 页面。document 是 window对象的属性,因此是一个全局对象。Document 类型的节点有以下特征:
- nodeType: 9
- nodeName: “#document”
- nodeValue: null
- parentNode: null
- ownerDocument: null
- 子节点可以是 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或 Comment 类型。
- 文档子节点: 虽然 DOM 规范规定 Document 节点的子节点可以是 DocumentType、Element、ProcessingInstruction 或 Comment,但也提供了两个访问子节点的快捷方式。第一个是 documentElement 属性,始终指向 HTML 页面中的元素。虽然 document.childNodes 中始终有元素,但使用 documentElement 属性可以更快更直接地访问该元素。假如有以下简单的页面:浏览器解析完这个页面之后,文档只有一个子节点,即元素。这个元素既可以通过documentElement 属性获取,也可以通过 childNodes 列表访问,如下所示:
1
2
3
4<html>
<body>
</body>
</html>作为 HTMLDocument 的实例,document 对象还有一个 body 属性,直接指向元素。因为这个元素是开发者使用最多的元素,所以 JavaScript 代码中经常可以看到 document.body,比如:1
2
3let html = document.documentElement; // 取得对<html>的引用
alert(html === document.childNodes[0]); // true
alert(html === document.firstChild); // trueDocument 类型另一种可能的子节点是 DocumentType。<!doctype>标签是文档中独立的部分,其信息可以通过 doctype 属性(在浏览器中是 document.doctype)来访问,比如:1
let body = document.body; // 取得对<body>的引用
另外,严格来讲出现在元素外面的注释也是文档的子节点,它们的类型是 Comment。不过,由于浏览器实现不同,这些注释不一定能被识别,或者表现可能不一致。比如以下 HTML 页面:1
let doctype = document.doctype; // 取得对<!doctype>的引用
1
2
3
4
5
6<!-- 第一条注释 -->
<html>
<body>
</body>
</html>
<!-- 第二条注释 --> - 文档信息: document 作为 HTMLDocument 的实例,还有一些标准 Document 对象上所没有的属性。这些属性提供浏览器所加载网页的信息。其中第一个属性是 title,包含
元素中的文本,通常显示在浏览器窗口或标签页的标题栏。通过这个属性可以读写页面的标题,修改后的标题也会反映在浏览器标题栏上。不过,修改 title 属性并不会改变 元素。下面是一个例子: 接下来要介绍的 3 个属性是 URL、domain 和 referrer。其中,URL 包含当前页面的完整 URL(地址栏中的 URL),domain 包含页面的域名,而 referrer 包含链接到当前页面的那个页面的 URL。如果当前页面没有来源,则 referrer 属性包含空字符串。所有这些信息都可以在请求的 HTTP 头部信息中获取,只是在 JavaScript 中通过这几个属性暴露出来而已,如下面的例子所示:1
2
3
4// 读取文档标题
let originalTitle = document.title;
// 修改文档标题
document.title = "New page title";1
2
3
4
5
6// 取得完整的 URL
let url = document.URL;
// 取得域名
let domain = document.domain;
// 取得来源
let referrer = document.referrer; - 定位元素: 使用 DOM 最常见的情形可能就是获取某个或某组元素的引用,然后对它们执行某些操作。document 对象上暴露了一些方法,可以实现这些操作。getElementById()和 getElementsByTagName()就是 Document 类型提供的两个方法。HTMLDocument 类型上定义的获取元素的第三个方法是getElementsByName()。顾名思义,这个方法会返回具有给定 name 属性的所有元素。
- 特殊集合: document 对象上还暴露了几个特殊集合,这些集合也都是 HTMLCollection 的实例。这些集合是访问文档中公共部分的快捷方式,列举如下。
document.anchors 包含文档中所有带 name 属性的元素。
document.applets 包含文档中所有 - DOM兼容性检测: document.implementation 属性是一个对象,其中提供了与浏览器 DOM 实现相关的信息和能力。
- 文档写入: document 对象有一个古老的能力,即向网页输出流中写入内容。这个能力对应 4 个方法:write()、writeln()、open()和 close()。其中,write()和 writeln()方法都接收一个字符串参数,可以将这个字符串写入网页中。write()简单地写入文本,而 writeln()还会在字符串末尾追加一个换行符(\n)。