(二)文档对象模型(DOM),你只需知道这些就够了!
官方定义——应用程序编程接口(API)
文档对象模型是用于HTML和XML文档的应用程序编程接口,它定义文档的逻辑结构,以及访问和操作文档的方式。
"The Document Object Model (DOM) is an application programming interface (API) for HTML and XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated." —— W3C DOM
“文档对象模型是用于HTML和XML文档的应用程序编程接口”
应用程序编程接口(Application Program Interface,简称API),就是“提供商”将一个功能的具体实现(业务逻辑)封装起来,只暴露给“客户”可供调用的编程接口。如果客户想使用某一功能,则只需调用该功能对应的编程接口,而无需关注于具体实现。打个比方,海尔生产空调,你购买空调后按照说明书操作空调就好,不需要知道空调是根据什么原理造出来的。
对于DOM而言,“提供商”指的就是实现DOM的开发者(比如,开发JavaScript引擎V8的工程师),“客户”指的就是使用DOM的开发者(比如,开发网站的前端工程师)。他们之间共同遵守的就是DOM规范。
因此,DOM规范同时约束它的实现者和使用者。DOM实现者必须实现规范中定义的所有接口,DOM使用者应该调用规范中相应的接口实现相应的功能。
如果你是实现者,需要关注如何实现DOM API;如果你是使用者,则只需关注如何使用DOM API。
“它定义文档的逻辑结构,以及访问和操作文档的方式”
上面这句话对 DOM API 作了更具体的解释,包括三层意思:
- 定义文档的结构
- 访问文档的方式
- 操作文档的方式
HTML DOM 是对 DOM Core 的扩展,添加了专门针对 HTML 文档的特定对象和方法,它与现有的文档对象模型一致。下面我们就来讨论如何使用 HTML DOM API 的问题。
一、如何定义文档的逻辑结构?——节点对象树
DOM规范,将网页中的HTML文档抽象为内存中的节点对象树(DOM Tree)。树中的每一个节点对象对应HTML文档中的一个元素。
二、如何访问文档?——访问节点对象
(0) Document 接口作为文档入口
Document 接口代表整个文档。JavaScript 中的全局对象 document 是对 Document 接口的实现。
(1) 直接访问文档中的节点对象。
方法 | 描述 |
document.getElementsById("id"); | 通过 id 属性获取节点对象 |
document.getElementsByName("name"); | 通过 name 属性获取节点对象 |
document/element.getElementsByTagName("tag"); | 通过 tag 属性获取节点对象 |
document/element.getElementsByClassName("class1[ class2]"); | 通过 class 属性获取节点对象 |
document/element.querySelector("selector"); | 通过 css 选择器 获取首个节点对象 |
document/element.querySelectorAll("selector"); | 通过 css 选择器 获取所有节点对象 |
(2) 通过节点关系访问一个节点周围的节点对象。
关系 | 属性 | 描述 |
父节点(parent node) | parentNode/parentElement | 获取所属父节点对象 |
ownerDocument | 获取节点所属文档节点(根节点)对象 | |
兄弟节点(sibling nodes) | nextSibling nextElementSibling |
获取前一个兄弟节点对象 获取前一个兄弟元素节点对象 |
previousSibling previousElementSibling |
获取后一个兄弟节点对象 获取后一个兄弟元素节点对象 |
|
子节点(child nodes) | childNodes children |
获取所有子节点对象 获取所有子元素节点对象 |
hasChildNodes(); | 判断是否包含子节点对象 | |
childElementCount | 获取子元素节点对象数量 | |
firstChild firstElementChild |
获取第一个子节点对象 获取第一个子元素节点对象 |
|
lastChild lastElementChild |
获取最后一个子节点对象 获取最后一个子元素节点对象 |
三、如何操作文档?——操作节点对象
思路一:【节点对象】首先是一个【对象】,只要是对象就具有【属性】和【行为】。
元素节点p作为对象,id,class,style(该属性是一个对象)等作为对象的属性,onclick,onfocus,oninput 等作为对象的行为,具体的动作由 JavaScript 代码控制。
思路二:节点对象只包含属性 —— 特性属性、样式属性和行为(事件)属性。
将【行为】看作对象的一种属性,以下实例印证了这一观点:
我们获取节点对象的目的,就是为了操作节点对象本身以及节点对象的属性。
(0) 操作节点对象本身
操作 | 方法 | 描述 |
创建节点 | document.createElement("TAG"); document.createTextNode("#文本"); document.createComment("#注释"); document.createDocumentFragment(); document.createAttribute("myAttrName"); |
创建一个元素节点 创建一个文本节点 创建一个注释节点 创建一个空白的文档片段节点 |
插入节点 | parentNode.appendChild(newChild); parentNode.insertBefore(newChild,refChild); |
插入子节点(作为最后子节点) 插入子节点(在指定子节点之前) |
删除节点 | parentNode.removeChild(oldChild); | 删除指定的子节点 |
替换节点 | parentNode.replaceChild(newChild,oldChild); | 替换一个子节点 |
克隆节点 | node.cloneNode(deep); | 克隆一个节点 |
(1) 操作节点对象的特性属性
方法 | 描述 |
e.特性名称[="value"]; | 获取/设置特性值 |
e.getAttributeNames(); | 获取已设置的全部属性名称(非IE内核) |
e.getAttribute("特性名称"); | 获取特性值 |
e.getAttributeNode("特性名称"); | 获取特性节点[特性名称=特性值] |
e.setAttribute("特性名称","特性值"); | 设置特性值(不存在时新建该属性) |
e.setAttributeNode(Attr); |
(2) 操作节点对象的样式属性
方法 | 描述 |
e.style.color[ ="颜色值"] | 获取/设置标签内样式(内嵌样式) |
window.getComputedStyle(e).color; document.defaultView.getComputedStyle(e).color; |
获取包括内嵌样式、<style>、<link>在内的最终样式(非IE内核 或 IE>8) |
e.currentStyle.color | 获取包括内嵌样式、<style>、<link>在内的最终样式(IE内核) |
(3) 操作节点对象的行为(事件)属性
方法 | 描述 |
e.onclick[ =fun()]; e.onclick(); |
获取/设置事件 触发事件 |
e.addEventListener("type",listener,[useCapture]); | 注册事件(非IE内核 或 IE>8) |
e.attachEvent("ontype",listener); | 注册事件(IE内核<11) |
e.removeEventListener("type",listener,[useCapture]); | 移除事件(非IE内核 或 IE>8) |
e.detachEvent("ontype",listener); | 移除事件(IE内核<11) |
e.dispatchEvent(event) | 调度(触发)事件 |
(4) 操作节点对象的内容(属性)
节点对象的内容,实际上是节点对象的子节点。传统的方法是使用新创建的子节点替换需要修改的旧子节点。
<html> <body> <div> <p>第一个段落</p> <p>第二个段落</p> </div> </body> </html>
<script> //获取父节点 var d = document.querySelector("div"); //获取需要替换的旧子节点 var oldChild = d.firstChild; //创建新子节点 var newChild = document.createTextNode("新的文本"); //替换 d.replaceChild(newChild ,oldChild ); </script>
如此常用的操作,却有如此繁琐的操作过程,况且一次只能替换一个子节点,这也太坑了吧!别急,DOM 规范为我们提供了更加简便的操作。
转换一下思路,我们将节点对象的内容看作是节点对象的一个属性。那么方法 e.innerHTML 属性将以字符串的形式返回所有节点对象的内容。 e.innerHTML = newStr 将替换该内容。newStr 可以包含tag,替换的内容会自动转换为节点对象(其实简单的文本字符串,也会转换成一个文本节点)。
<script> //获取节点 var d = document.querySelector("div"); //修改节点内容 var str = "新的文本"; d.innerHTML= str; </script>
最终的节点对象应该是下面这张图:
【The End】