学习笔记 2020-11-07
JavaScript 高级程序设计(第4版) 阅读记录
DOM
节点类型
Node 类型
操纵节点
因为所有关系指针都是只读的,所以 DOM 提供了一些操纵节点的方法。
appendChild():用于在 childNodes 列表末尾添加节点。
let returnedNode = someNode.appendChild(newNode); console.log(returnedNode == newNode); // true console.log(someNode.lastChild == newNode); // true
已存在的节点传给该方法,会使得该节点被转移到新位置。
insertBefore():用于把节点放到指定位置。接收两个参数,要插入的节点和参照节点。
// 作为最后一个子节点插入 returnedNode = someNode.insertBefore(newNode, null); console.log(newNode == someNode.lastChild); // true // 作为新的第一个子节点插入 returnedNode = someNode.insertBefore(newNode, someNode.firstChild); console.log(returnedNode == newNode); // true console.log(newNode == someNode.firstChild); // true // 插入最后一个子节点前面 returnedNode = someNode.insertBefore(newNode, someNode.lastChild); console.log(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true
replaceChild():替换节点。接收两个参数,要插入的节点和要替换的节点。返回要替换的节点。
// 替换第一个子节点 let returnedNode = someNode.replaceChild(newNode, someNode.firstChild); // 替换最后一个子节点 returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
removeChild():移除节点。接收要被移除的节点。返回被移除的节点。
// 删除第一个子节点 let formerFirstChild = someNode.removeChild(someNode.firstChild); // 删除最后一个子节点 let formerLastChild = someNode.removeChild(someNode.lastChild);
以上所有方法都用于操纵某节点的子元素。
其他方法
所有节点类型还共享了两个方法:
- cloneNode():返回与调用它的节点一模一样的节点,接收一个布尔值参数,表示是否深复制。深复制会复制节点以及整个子 DOM 树,浅复制只会复制调用该方法的节点。该方法不会复制添加到 DOM 节点的 JS 属性,例如事件处理程序。
- normalize():处理文档子树中的文本节点。在节点上调用该方法会检测这个节点的所有后代,从中搜索上述两种情形。删除空文本节点。合并两个相邻的同胞节点。
Document 类型
Document 类型是 JavaScript 中表示文档节点的类型。在浏览器中,文档对象 document 是 HTMLDocument 的实例 ( HTMLDocument 继承 Document ),表示整个 HTML 页面。document 是 window 对象的属性,是一个全局对象。Document 类型的节点有以下特征:
- nodeType 为 9;
- nodeName 值为
#document
; - nodeValue 值为 null ;
- parentNode 值为 null ;
- ownerDocument 值为 null;
- 子节点可以是 DocumentType ( 最多一个 )、Element ( 最多一个 )、ProcessingInstruction 或 Comment 类型。
Document 类型可以表示 HTML 页面或其他 XML 文档,但最常用的还是通过 HTMLDocument 的实例取得 document 对象。document 对象可用于获取关于页面的信息以及操纵其外观和底层结构。
文档子节点
Document 节点有两个访问子节点的快捷方式:
documentElement 属性,指向 HTML 页面的
<html>
元素。let html = document.documentElement; // 取得对<html>的引用 console.log(html === document.childNodes[0]); // true console.log(html === document.firstChild); // true
body 属性,指向
<body>
元素。
Document 类型另一种可能的子节点是 DocumentType 。
<!doctype>
标签是文档中独立的部分,其信息可以通过 doctype 属性来访问。文档类型是只读的,只能有一个 Element 类型的子节点,即
<html>
。文档信息
document 作为 HTMLDocument 的实例,还有一些标准 Document 对象上所没有的属性。
- title,包含
<title>
元素中的文本。通过这个属性可以读写页面的标题,但修改该元素不会改变<title>
元素。 - URL:包含当前页面的完整 URL 。
- domain:包含页面的域名。
- referrer:包含链接到当前页面的那个页面的 URL 。
以上三个属性,只有 domain 是可以设置的,且设置的值受限。
// 页面来自 p2p.wrox.com document.domain = 'wrox.com'; // 成功 document.domain = 'nczonline.net'; // 出错!
当页面中包含来自不同子域的窗格或内嵌窗格时,设置 document.domain 是有用的,可以避开跨域通信,使得彼此可以访问 JavaScript 对象。
浏览器对 domain 属性还有一个限制,这个属性一旦放松就不能再收紧。
// 页面来自 p2p.wrox.com document.domain = 'wrox.com'; // 放松,成功 document.domain = 'p2p.wrox.com'; // 收紧,错误!
- title,包含
定位元素
- getElementById() 接收一个参数,即要获取元素的 ID 。返回第一个匹配的元素或 null 。
- getElementsByTagName() 接收一个参数,即要获取元素的标签名。返回包含零个或多个元素的 NodeList 。在 HTML 文档中,这个方法返回一个 HTMLCollection 对象。HTMLCollection 对象与 NodeList 很相似。它有一个额外的方法 namedItem() ,可通过标签的 name 属性取得某一项的引用。对于有 name 属性的元素,还可以直接使用中括号来获取。
- getElementsByName() 接收一个参数,元素的 name 属性。
特殊集合
- document.anchors 包含文档中所有带 name 属性的
<a>
元素。 - document.applets 包含文档中所有
<applet>
元素。 - document.forms 包含文档中所有
<form>
元素。 - document.images 包含文档中所有
<img>
元素。 - document.links 包含文档中所有带 href 属性的
<a>
元素。
- document.anchors 包含文档中所有带 name 属性的
DOM 兼容性检测
DOM Level 1 在 document.implementation 上只定义了一个方法,即 hasFeature() 。这个方法接收两个参数:特性名称和 DOM 版本。
let hasXmlDom = document.implementation.hasFeature("XML", "1.0");
特性 支持的版本 说明 Core 1.0、2.0、3.0 定义树形文档结构的基本 DOM 。 XML 1.0、2.0、3.0 Core 的 XML 拓展,增加了对 CDATA 区块】处理指令和实体的支持。 HTML 1.0、2.0 XML 的 HTML 拓展,增加了 HTML 特点的元素和实体。 Views 2.0 文档基于某些样式的实现格式。 StyleSheets 2.0 文档的相关样式表 CSS 2.0 Cascading Style Sheets Level 1 CSS2 2.0 Cascading Style Sheets Level 2 Events 2.0、3.0 通用 DOM 事件 UIEvents 2.0、3.0 用户界面事件 TextEvents 3.0 文本输入设备触发的事件 MouseEvents 2.0、3.0 鼠标导致的事件 MutationEvents 2.0、3.0 DOM 树变化时触发的事件 MutationNameEvents 3.0 DOM 元素或元素属性被重命名时触发的事件 HTMLEvents 2.0 HTML 4.01 事件 Range 2.0 在 DOM 树中操作一定范围的对象和方法 Traversal 2.0 遍历 DOM 树的方法 LS 3.0 文件与 DOM 树之间的同步加载与保存 LS-Async 3.0 文件与 DOM 树之间的异步加载与保存 Validation 3.0 修改 DOM 树并保证其继续有效的方法 XPath 3.0 访问 XML 文档不同部分的语言 目前该方法已被废弃。
文档写入
- write()
- writeln()
- open()
- close()
write() 和 writeln() 都接收一个字符串参数,将这个字符串写入网页。
在页面加载过程中,两个方法会向文档中输出内容,在页面加载完后调用会重写整个页面。
open() 和 close() 用于打开和关闭网页输出流。
Element 类型
Element 表示 XML 或 HTML 元素,对外暴露出访问元素标签名、子节点和属性的能力。该类型的节点具有以下特征:
- nodeType 等于 1;
- nodeName 值为元素的标签名;
- nodeValue 值为 null ;
- parentNode 值为 Document 或 Element 对象;
- 子节点可以是 Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference 类型。
可以通过 nodeName 或 tagName 属性来获取元素的标签名。这两个属性返回同样的值。
在 HTML 中,元素标签名始终以全大写表示。
现代 JavaScript 教程
任务
存储 “unread” 标识
这里有一个 messages 数组:
let messages = [ {text: "Hello", from: "John"}, {text: "How goes?", from: "John"}, {text: "See you soon", from: "Alice"} ];
你的代码可以访问它,但是 message 是由其他人的代码管理的。该代码会定期添加新消息,删除旧消息,但是你不知道这些操作确切的发生时间。
现在,你应该使用什么数据结构来保存关于消息“是否已读”的信息?该结构必须很适合对给定的 message 对象给出“它读了吗?”的答案。
P.S. 当一个消息被从
messages
中删除后,它应该也从你的数据结构中消失。P.S. 我们不能修改 message 对象,例如向其添加我们的属性。因为它们是由其他人的代码管理的,我们修改该数据可能会导致不好的后果。
let messages = [ { text: 'Hello', from: 'John' }, { text: 'How goes?', from: 'John' }, { text: 'See you soon', from: 'Alice' } ]; let readMessages = new WeakSet(); // 两个消息已读 readMessages.add(messages[0]); readMessages.add(messages[1]); // readMessages 包含两个元素 // ……让我们再读一遍第一条消息! readMessages.add(messages[0]); // readMessages 仍然有两个不重复的元素 // 回答:message[0] 已读? console.log('Read message 0: ' + readMessages.has(messages[0])); // true messages.shift(); // 现在 readMessages 有一个元素(技术上来讲,内存可能稍后才会被清理)
保存阅读日期
这儿有一个和 上一个任务 类似的
messages
数组。场景也相似。let messages = [ {text: "Hello", from: "John"}, {text: "How goes?", from: "John"}, {text: "See you soon", from: "Alice"} ];
现在的问题是:你建议采用什么数据结构来保存信息:“消息是什么时候被阅读的?”。
在前一个任务中我们只需要保存“是/否”。现在我们需要保存日期,并且它应该在消息被垃圾回收时也被从内存中清除。
P.S. 日期可以存储为内建的
Date
类的对象,稍后我们将进行介绍。let messages = [ { text: 'Hello', from: 'John' }, { text: 'How goes?', from: 'John' }, { text: 'See you soon', from: 'Alice' } ]; let readMap = new WeakMap(); readMap.set(messages[0], new Date(2017, 1, 1));