第16章 DOM2和DOM3
# DOM的演进
isNameNode():比较两个完全相同的节点,即引用同一个对象。
isEqualNode():比较相等的节点,即节点类型相同,属性相等,也有类似的childNodes。
# 样式
# 存取元素样式
css属性表示:连字符分隔,如font-family
。
js属性表示:小驼峰形式,如style.fontFamily
。
js读取和修改css方式如下:
console.log(div.style.width)
div.style.width = '100px' // 必须写单位
2
DOM style属性和方法有:
cssText,包含style属性中的css代码。
length,应用给元素的css属性数量。
getPropertyValue(propertyName),返回属性propertyName的值。
item(index),返回索引为index的css属性名。
setProperty(propertyName,value,priority),设置css属性。
removeProperty(propertyName),从样式中移除css属性。
计算样式:document.defaultView.getComputedStyle(element,after)
可以获得元素及其伪元素的样式信息。
# 操作样式表
document.styleSheets:表示文档中可用的样式表集合。
CSSRule列表表示样式表中的规则集合。
insertRule():添加新规则。
deleteRule():删除规则。
# 元素尺寸
注意元素下面3类尺寸都是只读的,每次访问都会重新计算,注意性能。
# 1.偏移尺寸
表示元素在屏幕上占用的所有视觉空间,属性如下:
- offsetHeight,元素在垂直方向上占用的尺寸,包括高度、水平滚动条高度和上下边框的高度。
- offsetLeft,元素左边框外侧到包含元素的父盒子左边框内侧的距离。
- offsetTop,元素上边框外侧到包含元素的父盒子上边框内侧的距离。
- offsetWidth,元素在水平方向上占用的尺寸,包括宽度、垂直滚动条宽度和左右边框的高度。
# 2.客户端尺寸
表示元素内容及其内边距所占用的空间。
- clientWidth,内容宽度加左右内边距宽度。
- clientHeight,内容高度加上下内边距高度。
# 3.滚动尺寸
表示元素内容滚动距离的信息。
- scrollHeight,元素内容的总高度。
- scrollLeft,内容左侧隐藏的距离,设置这个属性可以改变元素的水平滚动位置。
- scrollTop,内容顶部隐藏的距离,设置这个属性可以改变元素的垂直滚动位置。
- scrollWidth,元素内容的总宽度。
# 4.确定元素尺寸
每个元素都有getBoundingClientRect()
方法,返回DOMRect对象,包含left/right,top/bottom,width/height总共6个属性,表示元素在页面中相对于视口的位置。
# 遍历DOM
有2个类型可以对DOM结构进行深度优先遍历。
# NodeIterator
document.createNodeIterator(root,whatToShow,filter,false)创建实例,可以进行遍历。
<ul class="ul">
<li class="li">1</li>
<li class="li">2</li>
<li class="li">3</li>
<li class="li">4</li>
<li class="li">5</li>
<li class="li">6</li>
<li class="li">7</li>
</ul>
2
3
4
5
6
7
8
9
const ul = document.querySelector('ul')
let iterator = document.createNodeIterator(
ul,
NodeFilter.SHOW_ELEMENT,
null,
false
)
let node = iterator.nextNode()
while (node !== null) {
console.log(node.tagName) // 输出所有标签名
node = iterator.nextNode()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# TreeWalker
假如只遍历li标签,则需要加上filter。
const ul = document.querySelector('ul')
const filter = function (node) {
return node.tagName.toLowerCase() === 'li'
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP
}
let iterator = document.createNodeIterator(
ul,
NodeFilter.SHOW_ELEMENT,
filter,
false
)
let node = iterator.nextNode()
while (node !== null) {
console.log(node.tagName) // 输出LI标签名
node = iterator.nextNode()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
是否有更简单的方法,省去过滤的操作?就是TreeWalker。因为它新增了DOM结构中向各个方向遍历的方法。
- parentNode(),遍历到当前节点的父节点。
- firstChild(),遍历到当前节点的第一个子节点。
- lastChild(),遍历到当前节点的最后一个子节点。
- nextSibling(),遍历到当前节点的下一个同胞节点。
- previousSibling(),遍历到当前节点的上一个同胞节点。
这赋予了TreeWalker在DOM结构中游走的能力。
const ul = document.querySelector('ul')
let walker = document.createTreeWalker(
ul,
NodeFilter.SHOW_ELEMENT,
null,
false
)
let node = walker.firstChild()
while(node !== null){
console.log(node.tagName)
node =walker.nextSibling()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 范围
范围用于在文档中选择内容,而不用考虑节点之间的界限。
# DOM范围
使用createRange()
可以创建DOM范围对象,如
let range = document.createRange()
这个范围对象不能在文档中直接使用,但可以对范围的内容执行一些操作,比如选择,插入,移除等等。
range实例的属性如下:
- startContainer,包含选区起始点的父节点
- endContainer,包含选区终点的父节点(如果在复杂选择中跨元素了,那起始和终点的父节点就不是同一个了)
- startOffset,范围起点在startContainer中的索引,即第几个节点
- endOffset,范围终点在startContainer终点索引
# 简单选择
- selectNode(),选择整个节点,包括其后代节点。
- selectNodeContents(),只选择节点的后代。
举个例子:
<body>
<p id="p1"><b>hello</b> world!</p>
</body>
2
3
let range1 = document.createRange(),
range2 = document.createRange(),
p1 = document.querySelector('#p1')
range1.selectNode(p1)
range2.selectNodeContents(p1)
console.log(range1)
console.log(range2)
2
3
4
5
6
7
8
这个例子中:
range1 | range2 | |
---|---|---|
startContainer | body | p#p1 |
startOffset | 1(前面有个空白文本节点) | 0 |
endOffset | 2 | 2 |
此外,选择范围后可以调用方法,从而实现更精细的控制。
- setStartBefore(refNode),把范围的起点设置到refNode之前,从而让refNode成为选区的第一个子节点。
- setStartAfter(refNode),把范围的起点设置到refNode之后,从而将refNode排除在选区之外。
- setEndBefore(refNode),把范围的终点设置到refNode之前,从而将refNode排除在选区之外。
- setEndAfter(refNode),把范围的终点设置到refNode之后,从而让refNode成为选区的最后一个子节点。
# 复杂选择
- setStart(refNode,offset),refNode(参照节点)会成为startContainer,offset(偏移量)赋值给startOffset。
- setEnd(refNode,offset),参照节点会成为endContainer,偏移量赋值给endOffset。
假设要选择上文中的"llo"到" wo"部分,代码如下:
let range = document.createRange(),
p1 = document.querySelector('#p1'),
helloNode = p1.firstChild.firstChild,
worldNode = p1.lastChild
range.setStart(helloNode, 2)
range.setEnd(worldNode, 3) // 注意要选择不属于选区的第一个字符的位置,这里是r的位置
console.log(range)
2
3
4
5
6
7
8
# 操作范围
前面说了简单和复杂选择两种选中范围的方式,后面就是对选区进行操作了。
如果是复杂选择导致选区缺少开始或结束标签,后台会动态补上,即
<p><b>He</b><b>llo</b> world!</p>
并且“ world!”文本节点也会被拆分成2个文本节点。
deleteContents(),删除选区。
range.deleteContents()
1效果如下
<p id="p1"><b>he</b>rld!</p>
1extractContents(),删除选区,会返回删除选区的文档片段。
const remain = range.extractContents() console.log(remain)
1
2cloneContents(),创建副本,这样就不会删除文档了。
const clone = range.cloneContents() const div = document.createElement('div') div.appendChild(clone) document.body.appendChild(div)
1
2
3
4
# 范围插入
有2种方式想范围插入内容:
insertNode(),在范围选区的开始位置插入一个节点。
... let span = document.createElement('span') span.style.backgroundColor = 'red' span.textContent = 'inserted text' range.insertNode(span)
1
2
3
4
5最终得到如下html
<p id="p1"><b>he<span style="background-color: red;">inserted text</span>llo</b> world!</p>
1surroundContents(),对选区内容进行包裹。
let range = document.createRange(), p1 = document.querySelector('#p1'), helloNode = p1.firstChild.firstChild range.selectNode(helloNode) let span = document.createElement('span') span.style.backgroundColor = 'red' range.surroundContents(span)
1
2
3
4
5
6
7
8
9得到如下html
<p id="p1"><b><span style="background-color: red;">hello</span></b> world!</p>
1
# 范围折叠
- collapse(boolean),true表示折叠到起点,false表示折叠到终点。
# 范围比较
- compareBoundaryPoints(),确定范围之间是否存在公共边界。
# 复制范围
- cloneRange()
# 清理
- detach(),从文档中剥离范围,解除对范围的引用。