什么是DOM?
- Document Object Model文档对象模型
- 针对HTML和XML文档的一个API(Application Programming Interface)
- 描绘了一个层次化的节点树,允许开发人员进行增删改页面的某一部分!
- 脱胎于NetScape及微软公司的DHTML(Dynamic HTML)
对应的发展历程?
- 1998年DOM 1级规范成为W3C推荐标准,为文档结构及查询提供了接口,主要是定了HTML和XML文档的底层结构
- DOM2在DOM1的基础上引入了更多的交互能力!
- DOM3引入了以统一方式加载和保存文档的方法!
01|节点层次
DOM可以将任何HTML或者说XML文档庙会成为一个由多层节点构成的结构!
- 节点分为不同的类型,每种类型分别表示文档中不同的信息或者说标记!
- 每次节点都有自己的特点,数据和方法!
对应的文档节点只有一个子节点,也就是所谓的<html>
元素,我们又称之为"文档元素",文档中的所有元素都包含在这个文档元素之中!
对应的每个文档只能够有一个文档元素!
- HTML中文档元素始终都是
<html>
- XML中没有预定义的文档元素!
02|Node类型
DOM1级定义了一个Node接口,该接口由DOM中的所有节点类型实现! Node接口在JavaScript中是作为Node类型实现的!
- 其他所有的浏览器(除了IE)都能访问到这个类型,JavaScript中的所有节点都继承自Node类型! 对应的所有节点类型都共享着相同的基本属性和方法
- 对应的每个节点都有其对应的
nodeType
属性,用于表明节点的类型!
节点类型由node类型中定义的12个数值常量进行表示:
- Node.ELEMENT_NODE(1); 元素节点
- Node.ATTRIBUTE_NODE(2);
- Node.TEXT_NODE(3); Element或者说Attr中实际的文字
- Node.CDATA_SECTION_NODE(4); 一个CDATASection 例如说:<!CDATA[[...]]>
- Node.ENTITY_REFERENCE_NODE(5);
- Node.ENTITY_NODE(6);
- Node.PROCESSING_INSTRUCTION_NODE(7); 一个用于XML文档的ProcessingInstruction(处理指令)
- Node.COMMENT_NODE(8); 一个注释节点
- Node.DOCUMENT_NODE(9); 一个文档节点
- Node.DOCUMENT_TYPE_NODE(10); 描述文档的DocumentType节点
- Node.DOCUMENT_FRAGMENT_NODE(11); 文档片段
- Node.NOTATION_NODE(12);
任何的节点必定是这其中的一种!
对应的被弃用的节点类型常量:
常量 | 值 | 描述 |
---|---|---|
Node.ATTRIBUTE_NODE |
2 |
元素 的耦合属性 。在 DOM4 规范里Node 接口将不再实现这个元素属性。 |
Node.ENTITY_REFERENCE_NODE |
5 | 一个 XML 实体引用节点。 在 DOM4 规范里被移除。 |
Node.ENTITY_NODE |
6 | 一个 XML <!ENTITY ...> 节点。 在 DOM4 规范中被移除。 |
Node.NOTATION_NODE |
12 | 一个 XML <!NOTATION ...> 节点。 在 DOM4 规范里被移除. |
其中有些时候可以利用nodeType与常量进行比较,例如someNode.nodeType===NODE>ELEMENT_NODE
相等的话对应的意味着someNode确实是一个元素!
这种情况适用于特定浏览器的使用,但是如果说需要保持兼容的话,可以直接和数字进行比较:
if(someNode.nodeType === 1){
console.log("node is an element!")
}
其中介绍到了nodeName和nodeValue的属性,两个属性的值完全取决于节点的类型
- 如果是元素的话,取得并且保存nodeName的值 始终保存的是元素的标签名 nodevalue保存的值始终为null
其中对应的节点关系,其实可以使用传统的家族关系来进行表示!
每个节点都有一个childNodes,其中保存这一个NodeList对象,NodeList是一种类数组,用于保存着一组有序的节点,可以通过位置访问这些节点! NodeList对象最奇特的地方在于,它实际上是基于DOM结构动态执行查询的结果,DOM结构的动态变化会自动反映在NodeList对象中!
IE8及更早的版本将NodeList实现为一个COM对象,我们不能够像JavaScript对象中那种对象,需要将NodeList转换为数组:
const convertArray = nodes=>{
let array = null;
try{
array = [].slice.call(nodes,0);
}catch(excepion{
array = [];
for(let i=0;i<nodes.length;i++){
array.push(nodes[i]);
}
}
return array;
}
对应的每一个节点都parentNode属性,该属性指向文档书中的父节点,包含在childNodes列表中的所有节点都具有相同的父节点! 因此对应的parentNode属性都指向同一个节点!
其中对应的两个API是访问对应的兄弟节点:
- previousSibling
- nextSibling
对应的一个父节点的 firstChild
和lastChild
分别指向NodeList中的第一个和最后一个节点!
- 父节点只有一个子节点,那么对应的
firstChild
和lastChild
都为同一个节点 - 如果说没有子节点,那么对应的firstChild和lastChild的值均为null!
其中有一个API可以很好地判断该节点的子节点是否存在! 如果子节点的数量为1或者更多的时候,那么就返回true!
对应的方法为:hasChildNodes
最后的一个属性就是ownerDocument可以指向整个文档的文档节点!
操作节点
DOM提供了一系列操作节点的方法,其中最最最常用的就属于appendChild方法了,用于向childNodes中添加一个节点!此时添加的节点就属于childNodes中的最后一个节点了!
如果说需要把特定的节点放到childNodes的某个节点之后就需要使用下面的API了:
- insertBefore
someNode.insertBefore(node,currentNode);//node:插入的节点 currentNode:表示参照的节点
someNode.insertBefore(node,null);//someNode的childNodes的最后位置添加一个节点node
someNode.insertBefore(node,someNode.firstChild);//插入到第一个节点的前面 作为第一个节点
someNode.insertBefore(node,someNode.lastChild);//插入到第最后节点的后面 作为最后的节点
- replaceChild
someNode.replaceChild(insertNode,replaceNode);//insertNode:插入的节点 replaceNode:替换的Node 返回被替换掉的节点
someNode.replaceChild(newNode,someNode.firstChild);//替换第一个节点
someNode.replaceChild(newNode,someNode.lastChild);//替换最后一个节点
- removeChild
someNode.removeChild(node);//移除指定的节点 并且被移除的节点成为返回值
someNode.removeChild(someNode.firstChild);//删除第一个子节点
someNode.removeChild(someNode.lastChild);//删除最后一个子节点
其他的方法有一个方法叫做cloneNode
作为克隆节点的方法,接受一个布尔类型的参数,如果说为true的话进行深克隆,复制整个节点和对应的子节点数,如果说为false的话,对应的复制节点本身!
对应的,cloneNode方法不会复制DOM节点中的JavaScript属性,例如说事件处理程序等等! 其他一切都不会复制,IE再次存在一个Bug,他会复制事件的处理程序,因此在使用该方法之前最好是先移除对应的事件处理程序!
02|Document类型
对应的JavaScript通过Document雷星表示文档,document对象是HTMLDocument的一个实例,表示整个HTML页面,并且document是window对象的一个属性!
对应的Document类型可以表示HTML页面或者说基于XML的文档,最最最常见的还是作为HTMLDocument实例的document对象!
- 该对象可以去的页面相关信息
- 操作页面的外观及其对应的底层结构!
- 文档的子节点
对应的DOM标准规定了Document的子节点可以是:
- DocumentType
- Element
- ProcessingInstruction
- Comment
但是对应的内置了两个可以访问子节点的方式如下所示:
- 通过documentElement
- 通过childNodes列表访问子节点
经常写HTML的人应该不陌生,对应的HTML页面经过浏览器解析之后,其实很明显的就能够知道,文档中只包含一个子节点,html元素:
下面是几种访问子节点的方式:
let html = document.documentElement;
console.log(html === document.childNodes[0]);
console.log(html === document.firstChild);
其实对应关系都是指向同一个字节点,HTML元素
其实Document对象中还包含了很多属性的,例如说:
- document.body //对应的可以直接访问body元素!
- document.doctype //取得对 文档类型的引用!
<!DOCTYPE>
- 文档信息的获取
对应的文档信息中的内容通过document对象的属性进行获取或者说设置,那么第一个我们常见的就是所谓的标题:
const title = document.title;
document.title = "Hello ProbeDream";
如果说是对应的url也是通过类似的方式进行获取或者说设置:
const url = document.URL;
document.URL = "https://github.com/probedream"
如果说要想获取域名:
const domain = document.domain;
如果说想获取来源页面的URL:
const referer = documnt.referer;
- 元素的获取
我们就在书中所提到的API中将几个比较常见的API提炼出来:
- document.getElementById(); //通过对应的id取得页面中对应ID的元素
- document.getElementsByTagName(); // 通过对应的标签名获取到对应的 HTMLCollection (元素集合)
对应的还有一个不怎么常见的API,叫做 namedItem 通过元素的name属性获取到元素集合中符合条件的元素!
如果说想获取页面中的所有元素,可以使用我们介绍到的 getElementsByTagName() 传入对应的 "*"
拿到对应的所有元素! 集合中以顺序的方式拿到对应的元素!
- 还有一个不常用的API,叫做 getElementByName 通过name属性拿到符合条件name的所有的元素集合!
- 特殊集合
除了对应的属性和方法,document对象还提供了一些特殊的集合,对应的集合都是为 HTMLCollection对象
本文中介绍到的也是快捷方式:
- document.anchors: 文档中所有带name特性的a元素
- document.forms: 文档中所有的form元素
- document.applet: 文档中所有的applet元素
- document.images: 文档中所有的img元素
- document.links: 文档中所有的具有href特性的a元素
- DOM一致性检测
DOM分为不同的级别,也包含了多个部分,因此对应的检测浏览器实现了DOM的哪些部分还是比较重要的,对应的文档对象提供了一个API,叫做:document.implementation
属性就是为此提供响应信息和功能的对象!
dom1级值为document.implementation提供了一个方法,为: hasFeature 表是否拥有该特性/功能!
- 文档写入
其实关于这一点: 文档写入的功能也是在看JS高程的时候才发现,不是常用的一个特性!
对应的有四个API:
- write 原样写入
- writeln 字符串末尾加上对应的换行符
- open 打开网页的输出流
- close 关闭网页的输出流
03|Element类型
除了Document类型之外,Element类型就要算是Web编程中最常用的类型了,Element类型用于表现XML或者说HTML元素,提供了对元素的标签名,子节点及特性访问!
如果说我们想要访问元素的标签名,可以使用两个属性,分别是:nodeName属性和对应的tagName属性会返回相同的值,但是明显后者更加符合语意!
- HTML元素
所有的HTML元素都有HTMLElement类型表示,而不是直接通过该类型,也是通过该类型的子类型进行表示,HTMLElement类型直接继承来自Element并且添加了一些属性,元素中添加的属性,我们都可以通过元素对象的属性获取:
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
const div = document.getElementById("myDiv");
console.log(`class属性:${div.class} title属性:${div.title} lang属性:${div.lang} dir属性:${div.ltr}`)
</script>
- 取得特性
每个元素都会有一个或者说多个特性,这些特性的用途其实就是给元素附加信息,操作特性的DOM操作方法有三个:
- getAttribute
- setAttribute
- removeAttribute
其实通过对应的API的名称就能够很清楚的知道如何使用了!
const div = document.getElementById("mydiv");
div.getAttribute("message");
div.getAttribute("attribute key","value");
div.removeAttribute("message");
对应的Element类型是使用attributes属性的唯一一个DOM节点类型,attributes属性中包含一个NamedNodeMap,与对应的NodeList相似,是一个动态的集合!
- 元素的每一个特性都会有Attr节点进行表示
- 每个节点都保存在NamedNodeMap对象中,NameNodeMap对象拥有下列方法
- getNamedItem(name) : 返回nodeName属性等于name的节点
- removeNamedItem(name): 从对应列表中移除nodeName属性等于name的节点
- setNamedItem(node) :向对应的列表中添加节点
- item(pos):返回位于数字position位置处的节点!
- 创建元素
对应的常见元素,就需要使用到我们最常见的API:document.createElementAPI了 对应的里面传入的是对应的标签名,如果说是在HTML中的话标签名不分大小写,如果说是在XML中的话对应的严格区分大小写!
04|Text类型
对应的文本节点其实就是有Text类型予以表示,不包含对应的HTML代码,对应的就是文本内容!
常用的API如下所示:
- createTextNode:创建文本节点! 传入要插入节点的文本!
05|Comment类型
对应的这就是所谓的注释类型了! 对应的开发中也很少用到! 我们做简单的了解不进行深入探讨了!
最常用的API也就所属:createComment里面传入对应的注释内容!
06|CDATASection类型
对应的CDATASection类型指针对基于XML的文档,表示的是CDATA区域! 继承自Text类型!
但是只会出现在XML文档中,HTML中并不会涉及,如果说涉及到XML的开发,可以使用document.createCDataSection创建对应的CDATA区域!
07|DocumentType类型
该类型在Web浏览器中并不常用,仅仅有Firefox,Safari,Opera中支持他!
08|DocumentFragment类型
其实从字面意思看出表示文档碎片的意思,是一种轻量级的文档,可以包含和控制节点,并不会向完整的文档那样占用额外的资源! 与此同时拥有Node的所有方法,通常用于执行那些针对文档的DOM操作!
其中介绍到的API:createDocumentFragment
09|Attr类型
元素的特性在DOM中以Attr类型进行表示,技术角度而言的话就是存在于元素attributes属性中的节点!
02|DOM操作技术
01|动态脚本
const loadScript = url=>{
const script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
loadScript("client.js");//加载脚本
但是对应的IE会出现问题,我们推出一个兼容性不错的方法:
const loadScriptString = code=>{
const script = document.createElement("script");
script.type = "text/javascript";
try{
script.appendChild(document.createElement(code));
}catch(exception){
script.text = code;
}
document.body.appendChild(script);
}
loadScriptString("const sayHi = ()=>{console.log('Hello!')}");
02|动态样式
const loadStyleString = css=>{
const style = document.createElement("style");
style.type = "text/css";
try{
style.appendChild(document.createTextNode(css));
} catch (ex){
style.styleSheet.cssText = css;
}
const head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
loadStyleString("body{background-color:red}");
03|小结
本文讲到的都是一些JavaScript中比较基础和关键的内容,之后的下一章会对其DOM内容作补充操作! 欢迎在评论区与我交流
网友评论