DOM
理解包含不同层次节点的DOM
使用不同的节点类型
克服浏览器兼容性问题以及各种陷阱
DOM描绘了一个层次化的节点数,允许开发人员添加 移除 修改页面的一部分
1.1 节点层次
DOM可以将HTML或XML描绘成一个多层节点构成的结构。
节点拥有不同的类型,每种类型表示文档中不同的信息,每个节点拥有各自的特点,数据和方法,另外与其他节点也存在某种关系。
总共有12种节点类型。
1.1.1 Node类型
DOM1级定义了一个Node接口,该接口将由DOM中所有节点类型实现。
js中的所有节点类型都继承自Node类型,因此所有节点类型都享有共同的属性和方法。
每个节点都有一个 nodeType属性 ,用于表明节点的类型。
比较nodeType 和 Node.ELEMENT_NODE 常量 如果二者相等 则意味着taobao确实是一个元素
<div id="taobao">taobao</div>
console.log(Node.ELEMENT_NODE) // 1
console.log(Node.ATTRIBUTE_NODE) // 2
console.log(Node.TEXT_NODE) // 3
console.log(Node.CDATA_SECTION_NODE) // 4
console.log(Node.ENTITY_REFERENCE_NODE) //5
console.log(Node.ENTITY_NODE) //6
console.log(Node.PROCESSING_INSTRUCTION_NODE) //7
console.log(Node.COMMENT_NODE) // 8
console.log(Node.DOCUMENT_NODE) //9
console.log(Node.DOCUMENT_TYPE_NODE)//10
console.log(Node.DOCUMENT_FRAGMENT_NODE) //11
console.log(Node.NOTATION_NODE) //12
let taobao = document.getElementById("taobao")
console.log(taobao.nodeType) // 1
if(taobao.nodeType == Node.ELEMENT_NODE){
console.log('taobao is an ELEMENT')
}
- nodeName 和 nodeValue属性
对于元素节点
nodeName 中保存的始终都是元素标签名
nodeValue始终都是null
console.log(taobao.nodeValue) // null
console.log(taobao.nodeName) // 元素的标签名 DIV
- 节点关系
每个节点都有一个childNodes属性,其中保存着NodeList对象,NodeList是一个类数组对象。
访问NodeList中的节点,可以通过方括号,也可以通过item()
let all = document.getElementById("all")
console.log(all.childNodes) // 其中保存着NodeList对象,NodeList是一个类数组对象。
console.log(all.childNodes.length)
console.log(all.childNodes[1]) // 方括号和item(效果一样)
console.log(all.childNodes.item(1))
父节点 parentNode
兄弟节点 previousSibling nextSibling
第一个孩子节点
最后一个孩子节点
console.log(taobao.parentNode) // 父亲节点
console.log(taobao.previousSibling) // 上一个兄弟节点
console.log(taobao.nextSibling) // 下一个兄弟节点
console.log(all.childNodes[0]) //第一个孩子节点
console.log(all.firstChild) // 第一个孩子节点
let nodeArr = Array.prototype.slice.call(all.childNodes , 0); // 将类数组对象转为数组
console.log(all.childNodes[all.childNodes.length - 1]) // 最后一个孩子节点
console.log(all.lastChild) //最后一个孩子节点
- 操作节点
appendChild()
在childNodes末尾添加一个节点,返回新增的节点。如果传入到appendChild()中的节点已经是文档的一部分了,那会将该节点移到新位置
insertBefore()
传入两个参数,要插入的节点,作为参照的节点。插入节点后,被插入的节点会变成参照节点的一个preciousSiblings节点
如果参照节点是null,那么效果和appendChild()一样
replaceChild()
传入两个参数,要插入的节点和要替换的节点
removeChild()
只接一个参数,要移除的节点
上面四个方法操作的都是某个节点的子节点,所以必须先取得父节点
ownerDocument()
该属性表示整个文档的文档节点。
// 操作节点
let all = document.getElementById("all")
let xianyu = document.getElementById("xianyu")
let tianmao = document.getElementById("tianmao")
all.appendChild(tianmao)
all.insertBefore(tianmao , xianyu)
all.replaceChild(tianmao , xianyu)
all.removeChild(tianmao)
- 其他方法
cloneNode()
接受一个布尔值,表示是否深复制
true 深复制 会赋值整个节点以及其子节点数
false 浅赋值,只复制节点本身
let anotherXianyu = xianyu.cloneNode(true); // 深复制
let anotherXianyu1 = xianyu.cloneNode(false); // 浅复制
console.log(anotherXianyu)
console.log(anotherXianyu1)
1.1.2 Document类型
js通过Document类型表示文档。在浏览器中,document是HTMLDocument的一个实例,表示整个页面。
而且document是window对象的一个属性,因此可以通过全局对象来访问。
Document节点具有下列特征
console.log(document.nodeName) // #document
console.log(document.nodeValue) // null
console.log(document.parentElement) // null
console.log(document.parentNode) // null
console.log(document.ownerDocument) //null
- 文档的子节点
document.documentElement // <html>
document.body // <body>
- 文档信息
console.log(document.title)
console.log(document.URL)
console.log(document.domain)
- 查找元素
getElementById()
document.getElementById("")
document.getElementsByName("")
document.getElementsByTagName("")
-
特殊集合
-
DOM一致性检测
-
文档写入
document.open()
document.write()
document.writeln()
document.close()
1.1.3 Element类型
- HTML元素
每个HTML元素都有以下特性
id title dir className lang
<div id="id" class="black" dir="ltr" title="标题" lang="en">中国</div>
let ele = document.getElementById("id")
console.log(ele.id) //
console.log(ele.className) // black
console.log(ele.dir) // 文字对齐方式
console.log(ele.lang) // en
console.log(ele.title) // 标题
setTimeout(()=>{
ele.className = "red";
ele.dir="rtl"
} , 1000)
- 取得特性
通常直接获得特性,只有在获取自定义属性时才会用getAttribute()
<div id="id" class="black" dir="ltr" title="标题" lang="en" myName="wujie">中国</div>
let ele = document.getElementById("id")
console.log(ele.getAttribute("id"))
console.log(ele.getAttribute("class"))
console.log(ele.getAttribute("title"))
console.log(ele.getAttribute("dir"))
console.log(ele.getAttribute("lang"))
// 获取自定义属性
console.log(ele.getAttribute("myName")) //wujie
console.log(ele.myName) // undefined
- 设置特性
setAttribute() 既可以设置HTML特性,也可以设置自定义属性
<div id="id" class="black" dir="ltr" title="标题" lang="en" myName="wujie">中国</div>
let ele = document.getElementById("id")
ele.setAttribute("class" , "red");
ele.setAttribute("title" , "新标题");
ele.myTitle = '我的标题'; // 直接设置自定义特性是不行的
console.log(ele.getAttribute("myTitle")) // null
ele.setAttribute("myTitle" , "我的标题")
console.log(ele.getAttribute("myTitle")) // 我的标题
彻底删除特性 以及特性的值
removeAttribute()
- attributes属性
Element 类型时唯一使用attributes属性的DOM 节点类型。
attributes包含元素的每一个特性以及部分方法
attributes通常用来遍历元素的所有属性
- 创建元素
document.createElement()可以创建元素
这个方法只接受一个参数,就是元素的标签名
然后通过
appendChild
insertBefore
replaceChild
把元素放进
let ele = document.getElementById("id")
let div = document.createElement("div")
div.className = "red";
div.innerHTML = "createElement"
ele.appendChild(div)
- 元素的子节点
由于文本 注释 等等也算子节点 ,所以获取元素的子节点时需要注意。
let ul = document.getElementsByTagName("ul")[0]
let ulChildArr = Array.prototype.slice.call(ul.childNodes , 0) // 将childNodes转为数组
console.log(ulChildArr) // [text, li, text, li, text, li, text]
// 过滤掉其他节点 只留下元素节点 即 nodeType 为1 的节点
let ulChildEleArr = ulChildArr.filter((item , index , arr) => {
return item.nodeType == '1'
})
console.log(ulChildEleArr) // [li, li, li]
1.1.4 Text类型
nodeType 为 3
nodeValue 为文本
nodeName 为 #text
parentNode 是一个 Element
<div id="txt-div">这是一个文本</div>
// 文本节点
let txtDiv = document.getElementById("txt-div");
// [text, div, text]
let textNode = txtDiv.childNodes[0]
console.log(textNode.nodeName) // #text
console.log(textNode.nodeType) // 3
console.log(textNode.nodeValue) // 这是一个文本
console.log(textNode.parentNode)
-
创建文本节点
-
规范化文本节点
-
分割文本节点
1.1.5 Comment节点
注释节点
1.1.6 CDATASection类型
针对的是XML文档
1.1.7 DocumentType
1.1.8 DocumentFragment
1.1.9 Attr类型
元素的特性在DOM中以Attr类型表示
从技术角度讲 特性就是元素中attributes属性的节点
不建议直接访问特性节点
直接使用 setAtribute getAttribute removeAttribute更方便
1.2 DOM操作技术
1.2.1 动态脚本
// 例如动态加入一个
let script = document.createElement("script");
script.type = "text/javascript";
script.src = './node.js';
document.appendChild(script)
1.2.2 动态样式
与动态脚本类似 不过<link>需要加到<head></head>里面
// 封装成函数
function loadStyle(url){
let style = document.createElement("link")
style.rel = "stylesheet";
style.type = "text/css";
style.href = url;
let head = document.getElementsByTagName("head")[0];
head.appendChild(style)
}
1.2.3 操作表格
1.2.4 使用NodeList
理解 NodeList 以及其近亲 NameNodeMap HTMLCollection是理解DOM的关键
NodeList是动态的,每次访问NodeList都会运行一次基于文档的查询
1.3 小结
DOM由各种节点组成,简要总结如下
- 最基本的节点是Node,用于抽象的表示文档中独立的一部分,所有其他类型都继承自Node
- Document类型表示整个文档。在js中,document是Document的一个实例,
使用document对象,有很多方式可以查询和取得节点 - Element类型表示文档中所有HTML或XML元素
理解DOM就是理解DOM对性能的影响,DOM操作是js中开销最大的部分。
而因访问NodeList导致的问题最多,因为NodeList是动态的,每次访问NodeList都会运行一次基于文档的查询
所以尽量要减少DOM操作