之前详细讲解了DOM1的内容,DOM2和DOM3在这些结构上加入了更多的交互能力.DOM2与DOM3是按照模块化的思路来制定标准的,每个模块之间有一定关联,但分别针对某个DOM子集.
DOM Core :在DOM核心部分的基础上,为节点增加方法和属性.
DOM Views :定义基于样式信息的不同视图.
DOM Events :定义通过事件实现DOM文档交互.
DOM Style :定义以编程方式访问和修改CSS样式的接口
DOM Traversal and Range :新增遍历DOM文档及其选择文档内容的接口
DOM Html :在DOM1HTML部分的基础上,增加属性,方法和新接口
DOM Mutation Observers :定义基于DOM变化触发回调的接口
DOM的演进
XML命名空间
XML命名空间可以实现在一个格式规范的文档中混用不同的XML语言.而不必担心元素命名冲突.
命名空间使用xmlns指定的.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Title goes here</title>
</head>
<body>
</body>
</html>
这个例子来说,所有元素都默认属于XHTML命名空间.
可以使用xmlns给命名空间创建一个前缀,格式为”xmlns:前缀”
<xm:html xmlns:xm="http://www.w3.org/1999/xhtml">
<xm:head>
<xm:title>Title goes here</title>
</head>
<xm:body>
</body>
</html>
当一个文档混合使用多种XML语言,有必要使用命名空间.
-
Node的变化
DOM2中,Node类型特定于命名空间的属性:
localName,不包含命名空间前缀的节点名
namespaceURI,节点的命名空间url.如果未指定,则值为null
prefix,命名空间前缀,如果未指定则为null.
如果节点使用命名空间前缀,则nodeName为prefix+’:’+localName.
DOM3中,进一步增加了如下与命名空间相关的方法:
isDefaultNamespaceURI(namespaceURI),返回布尔值,表示namespaceURI是否为节点的默认命名空间.
lookupNamespaceURI(prefix),返回prefix命名空间前缀的命名空间URL.
lookupPrefix(namespace),返回namespace命名空间url的prefix命名空间前缀.
-
Document的变化
DOM2中,Dcument类型新增命名空间的方法:
createElementNS(nameSpaceURI,tagName),以给定的标签名tagName创建指定命名空间namespaceURI的一个新元素.
createAttributeNS(nameSpaceURI,attributeName),以给定的属性名attributeName创建指定命名空间namespaceURI的节点的一个新属性.
getElementsByTagnameNS(namespaceURI,tagName),返回命名空间namespaceURI中的所有标签名为tagName的元素的NodeList
这些命名空间特定方法只有在文档中包含两个或两个以上命名空间时,才有用.
-
Element的变化
getAttributeNS(namespaceURI,attributeName)
setAttributeNS(namespaceURI,attributeName,value)
hasAttributeNS(namespaceURI,attributeName)
removeAttributeNS(namespaceURI,attributeName)
getAttributeNodeNS(namespaceURI,attributeName)
setAttributeNodeNS(namespaceURI,attributeName,value)
getElementsByTagnameNS(namespaceURI,tagName)
-
NamedNodeMap的变化
getNamedItemNS(namespaceURI,attributeName)
removeNamedItemNS(namespaceURI,attributeName)
setNamedItemNS(node)
其他变化
除命名空间相关的变化,DOM2 Core还对DOM的其他部分做了一些更新.
-
DocumentType变化
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
[<!ELEMENT name (#PCDATA)>]
>
<html>
<head>
<title>simple document</title>
</head>
<body>
<p>a simple paragraph</p>
</body>
</html>
DocumentType新增了3个属性:
pubilcId:”-//W3C//DTD XHTML 1.0 Strict//EN”
systemId:”
http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”
internalSubset:'[<!ELEMENT name (#PCDATA)>]’
-
Document的变化
-
importNode()方法
importNode()调用importNode()导入其他文档的节点会返回一个新节点,这个节点的ownerDocument属性是正确的.如果使用appendChild()方法时,传入节点的ownerDocument不是指定当前文档,则会发生错误.
importNode()接收两个参数,要复制的参数和表示是否同时复制子树的布尔值.与cloneNode相似.
一般在XML文档中使用.
-
Document.defaultView返回指向当前文档的窗口
-
Document.implementation对象
添加的对象方法:
createDocumentType(),创建DocumentType新节点.
3个参数:文档类型名称,pubilcId,systemId
createDocument(),创建Document新节点.
3个参数:文档元素的namespaceURI,文档元素的标签名和文档类型
要创建新文档要使用createDocument()方法.
DOM HTML模块向document.implamentation对象添加了createHTMLDocument()方法.
这个方法只接受一个参数,即新创建文档的标题(放在<title>标签内),返回一个新的HTML文档.
-
Node的变化
DOM3增加了两个用于比较节点的方法
:
isSameNode()
和
isEqualNode()
,
这两个方法都接收一个节点参数,如果这个节点与参考节点相同或相等,则返回true.
setUserData()
给DOM节点添加额外的数据方法.setUserData()方法接收3个参数:键,值,处理函数,用于给节点追加数据.
处理函数接收5个参数:表示操作类型的数值(1为复制,2为导入,3为删除,4为重命名),数据的键,数据的值,源节点和目标节点.删除节点时,源节点为null,除复制外,目标节点都为null.
-
内嵌窗格的变化
给HTMLIFrameElement类型新增了一个属性contentDocument.这个属性包含代表子内嵌网格中内容的document对象的指针.contentWindow返回相应窗口的window对象,这个对象上有一个document属性.
样式
HTML中的样式有3种定义方式:外部样式表,文档样式表,元素特定样式
存取元素样式
style属性
let myDiv=document.getElementById('d1')
myDiv.style.width='100px'
-
DOM样式属性和方法
cssText:可以访问到style特性(行内样式)中的CSS代码
length:应用给当前元素的CSS属性数量
parentRule:表示CSS信息的CSSRule对象
getPropertyValue(属性名):返回包含给定属性值的CSSValue对象
getPropertyPriority(属性名):若该属性使用了 !important 则返回 “important” 否则返回空字符串
getPropertyName(属性名):返回指定属性的字符串
item(下标):返回指定位置的CSS属性名称
removeProperty(属性名):从样式中删除给定属性
setProperty(属性名,属性值,优先级):设置属性,优先级传入”important“或空字符串
PS.以上属性中对csssText的重写将会覆盖原来style特性的值
-
计算样式
之前我们提到了,style对象的属性都只包含style特性的值,也就是行内样式.
这样可以让我们JS代码对元素样式的修改以较高的优先级覆盖掉外部样式和嵌入样式的影响.
但是这样为我们获取元素的样式带来了困难,所以DOM2增强了document.defaultView,新增了
getComputedStyle()
方法用于获取元素的准确样式
getComputedStyle方法传入两个参数:1、要取得样式的元素 2、伪元素字符串(如”:after”,若不需要伪元素信息则可以传入null)
该方法返回一个CSSStyleDeclaration对象
该对象包含当前元素所有计算后的样式,也就是呈现在页面上的样式信息.
虽然大多数浏览器都支持但是不同浏览器表示值的方式可能有所出入,所以在使用时需要注意.
不能修改getComputedStyle()方法返回的对象.
操作样式表
在JS中样式表用一种类型来表示,以便我们在JS对其进行操作
这一类型就是CSSStyleSheet 即CSS样式表类型,包括了之前 style 对象所不包括的外部样式表以及嵌入样式表
其中<link>以HTMLLinkElement 类型表示
而<style> 以HTMLStyleELement类型表示
但是这两类样式表更加通用的类型则是继承自StyleSheet 类型的 CSSStyleSheet
其中继承自StyleSheet 类型的有以下属性:
disabled:表示样式表是否被禁用,该属性可写
href:若是通过<link>引入的样式表,该属性的值则为样式表的 URL 否则为 null
media:当前样式表支持的媒体集合类型,若该集合为空列表则表示样式表适用于所有媒体
ownerNode:指向拥有当前样式表的节点的指针,若样式表通过 @import 导入则该属性的值为 null
parentStyleSheet:若当前样式表通过 @import 引入则指向引入它的样式表
title:ownerNode中的title属性的值
type:表示样式表类型的字符串,如“type/css”
这些属性除了disabled,其他的属性都是只读的.
CSSStyleSheet类型还支持以下属性和方法:
cssRules:样式表中包含样式规则的集合
ownerRule:若通过@imoprt 引入则指向导入的规则
deleteRule(index):删除cssRules中指定位置的规则
insertRule(rule,index):向cssRules指定位置插入规则
document.styleSheets表示文档中可用的样式表集合.length属性保存着样式表的数量.
document.styleSheets[0];
-
css样式规则
CSSRule 对象表示样式表中的每一条规则
CSSRule 是一个供其它多种类型继承的基类,其中CSSStyleRule就继承自该基类,用于表示样式表信息
CSSStyleRule拥有以下属性:
-
cssText:返回整条规则的对应文本
-
parentRule:如果当前规则是导入的规则,则这个属性就是导入的规则,否则为null
-
parentStyleSheet:当前规则所属样式表
-
selectorText:返回当前规则的选择符文本
-
style:一个CSSStyleDeclaration 对象,可以通过该对象修改设置和取得规则中的值
-
type:表示规则类型的常量值,对于CSS规则来说该值为1
PS.:这里的cssText属性与style对象的cssText存在差异,也就是这里的cssText不能进行写操作,而style对象的cssText可以被重写
这里和style对象的区别在于,style修改的样式只应用于当前元素,而CSSRule修改的规则则会应用于所有被选择符选中的元素
-
创建规则
insertRule()向样式表中添加新的规则.参数1为规则的文本,参数2为插入位置.
document.insertRule("body {background-color:red}",0)
添加规则,不如使用动态样式加载技术.
-
删除规则
deleteRule()接收一个参数:要删除规则的索引.
元素尺寸
-
偏移尺寸
offsetHeight:元素的高度(包含元素的外边框及水平滚动条)
offsetWidth:元素的宽度(包含元素的外边框及垂直滚动条)
offsetTop:元素的上边框到包含当前元素的元素的内上边框的距离
offsetLeft:元素的左边框到包含当前元素的元素的内左边框的距离
offsetParent:指向包含当元素的元素
offsetParent不一定是parentNode比如<td>元素的offsetParent是作为祖先的<table>元素
div元素中的所有元素都以<body>元素为其offsetParent
-
客户端尺寸
客户端尺寸包含元素内容及其内边距所占的空间,常用于检测浏览器视口.
clientHeight 内容区高度加内边距
clientWidth内容区宽度加内边距
-
滚动尺寸
scrollHeight:在没有滚动条的情况下,元素内容的总高度
scrollWidth:在没有滚动条的情况下,元素内容总宽度
scrollTop:隐藏在元素内容区域上方的像素数
scrollLeft:隐藏在元素内容区域左侧的像素数
-
确定元素尺寸
getBoundingClientRect()方法,返回一个DOMRect对象.包含6个属性.left,top,right,bottom,height
,width.
遍历
DOM2 Traveral and Range模块定义了两个类型用于辅助顺序遍历DOM结构.
NodeIterator和TreeWalker
NodeIterator
NodeIterator类型,可以通过document.createNodeIterator()方法创建其实例.
参数:
root,作为遍历根节点的节点.
whatToShow,数值代码,表示应该访问那些节点.
filter, NodeFilter对象或函数,表示是否跳过或接收特定节点.
entityReferenceExpansion,布尔值,表示是否接收或跳出特定节点.
whatToShow:数值代码
这些值以常量的形式定义在NodeFilter 类型
NodeFilter.SHOW_ALL:显示所有类型的节点
NodeFilter.SHOW_ELEMENT:显示元素节点
NodeFilter.SHOW_ATTRIBUTE:显示特性节点(由于DOM结构的原因在实际使用时并不能使用该值)
NodeFilter.SHOW_TEXT:显示文本节点
NodeFilter.SHOW_CDATA_SETCTION:显示CDATA节点对HTML页面没有作用
NodeFilter.SHOW_ENTITY_REFERENCE:显示实体引用节点,对HTML页面不起作用
NodeFilter.SHOW_ENTITY:显示实体节点,对HTML页面不起作用
NodeFilter.SHOW_PROCESSING_INSTRUCTION:显示指令处理节点对HTML页面不起作用
NodeFilter.SHOW_COMMENT:显示注释节点
NodeFilter.SHOW_DOCUMENT:显示文档节点
NodeFilter.SHOW_DOCUMENT_TYPE:显示文档类型节点
NodeFilter.SHOW_DOCUMENT_FRAGMENT:显示文档片段节点,对HTML页面无效
NodeFilter.SHOW_NOTATION:显示符号节点
以上常量可以使用位操作符来组合(除SHOW_ALL外),如
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT
filter:一个Node Filter对象,或一个表示接受或拒绝节点的函数
filter参数可以用来指定自定义NodeFilter对象,NodeFilter对象只有一个方法
acceptNode()
,如果给定节点应该访问就返回NodeFilter.FILTER_ACCEPT,否则就返回NodeFilter.FILTER_SKIP.
var filter = {
acceptNode:function(node){
return node.tagName.toLowerCase() === "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
}
var filter = function(node){
return node.tagName.toLowerCase() === "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
方法:previousNode()方法后退一步,nextNode()方法前进一步
TreeWalker
TreeWalker是NodeIterator 的高级版本
其创建方式和NodeIterator 一致
除了NodeIterator 所拥有的 nextNode、previousNode之外
TreeWalker还拥有以下方法
-
parent Node():得到当前节点的父节点
-
first Child():得到当前节点的第一个子节点
-
last Child():当前节点的最后一个子节点
-
nextSibling():当前节点的下一个同辈节点
-
previousSibling():当前节点的上一个同辈节点
其构建方法为:document.createTreeWalker()
还有一点不同在于TreeWalker的filter 可以使用 NodeFilter.FILTER_REJECT
其与Node FIlter.FILTER_SKIP 的区别在于 SKIP 只会跳过当前节点,而REJECT会跳过当前节点及其子树
除此而外TreeWalk 还有一个 currentNode 属性可以修改起始节点
范围
DOM范围
DOM2级在Document类型中定义了 createRange()方法.
在兼容该接口的浏览器中,该方法属于document对象.
如果浏览器支持范围,那么就可以使用createRange()来创建范围.
var range = document.createRange()
与节点类似,创建的范围也会和文档关联,不能用于其它文档.
每个范围由一个Range类型的实例表示.
下列
属性
提供了范围在文档中的位置信息.
-
startContainer:包含范围起点的节点(即选区中第一个节点的父节点)
-
startOffset:范围起点在startContainer 中的偏移量
-
endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)
-
endOffset:范围终点在endContainer中的偏移量
-
commonAncestorContainer:距离起点和终点最近的公共祖先节点
简单选择
使用范围来选择文档中的一部分,最简单的方式就是通过
selectNode() 和 selectNodeContents()方法
这两个方法都接收一个参数,即一个DOM节点
selectNode 是将传入节点的整个节点作为范围
selectNodeContents则是将传入节点的所有子节点作为范围
例如:
<p id = "p1"><b>hello</b>world!</p>
对于以上HTML代码
使用以下代码创建范围
var range1 = document.createRange();var range2 = document.createRange();var p1 = document.getElementById('p1');
range1 = range1.selectNode(p1);
range2 = range2.selectNodeContents(p1);
对于上述代码
range1包含的文档内容如下:
<p id = “p1”><b>hello</b>world!</p>
range2包含的文档内容如下:
<b>hello</b>world!
此外,为了更加精细地控制将哪些节点包含在范围中,还可以使用以下方法
-
setStartBefore(refNode):将范围起点设置在 refNode 之前(也就是refNode作为范围的第一个子节点)
-
setStartAfter(refNode):将范围起点设置在 refNode 之后(即refNode的下一个同辈节点作为范围的第一个子节点)
-
setEndBefore(refNode):将范围终点设置在 refNode 之前
-
setEndAfter(refNode):将范围终点设置在 refNode 之后
使用这里提到的方法会自动设置范围属性,当然也可以通过设置范围属性来改变选区的范围
即startContainer、startOffset、endContainer、endOffset、commonAncestorContainer这些选区属性
复杂选择
当我们需要更加复杂的选区,比如我们需要选择某个节点的一部分时
要创建这样复杂的节点,则需要使用setStart()和 setEnd()来分别设置范围的起止位置
这两个方法都接收两个参数:
1.DOM节点
2开始/结束位置在节点中的偏移量
<p id = "p1"><b>hello</b>world!</p>
var range1 = document.createRange();
var range2 = document.createRange();
var p1 = document.getElementById('p1');
var Index = -1;//用于表示p1在其父节点中的偏移
for(let i = 0,len = p1.parentNode.childNodes.length;i<len;i++){
if(p1.parentNode.childNodes[i] === p1){
Index = i;
break;
}
}
range1.setStart(p1.parentNode, Index);
range1.setEnd(p1.parentNode, Index+1);
range2.setStart(p1, 0);
range2.setEnd(p1, p1.childNodes.length);
操作范围
在创建范围时,内部会为这个范围创建一个文档片段
范围所属的全部节点都会被添加到这个片段中
虽然选取范围可以不是完整的、良好的DOM结构
但是在这个为范围创建的文档片段中,会自己完缺少的闭合标签,以此构建有效的DOM结构来方便我们操作
上述步骤都是内部实现的,因此我们可以不用过多地关注这一方面
首先是deleteContents()
这个方法会从文档中删除选中范围的内容
var p1 = document.getElementById("p1");
var helloNode = p1.firstChild.firstChild;
var worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode,2);
range.setEnd(worldNode,3);
range.deleContents();
<p id = "p1"><b>he</b>rld!</p>
由于在底层实现中JS会自动完整没有闭合的标签,所以能保有一个良好的文档结构
然后就是extractContents()方法
这个方法和delete Contents()方法一样都会从文档中移除范围内容
但是有所区别
这个方法会返回被移除的范围中的内容
此外cloneContents() 方法用于复制范围中的节点
和extractContents() 一样都会返回节点,只不过这里返回的不是实际节点
而是实际节点的副本
我们可以使用appendChild()方法将其重新插入文档中
PS.在调用上述的操作范围的方法之前,范围中的内容并不会产生格式良好的文档片段
范围插入
首先是 insertNode()方法,可以向范围的开始处插入一个节点
var p1 = document.getElementById("p1");
var helloNode = p1.firstChild.firstChild;
var worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode,2);
range.setEnd(worldNode,3);
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(document.createTextNode("Inserted text"));
range.insertNode(span);
运行后会得到以下文档结构
<p id = "p1"><b>he<spand style="color:red">Interted text</span>llo</b>world!</p>
此外还有一个方法surroundContents()用于环绕范围插入内容
range.selectNode(helloNode)
let span =document.createElement('span')
span.style.backgroundColor='yellow'
range.surroundContents(span)
一般来说用于为范围添加特殊样式
范围折叠
折叠是指范围没有选中内容的情况,就相当于用鼠标选择文字时的光标竖线一样
该情况储存在range 的 collapsed 属性中.
折叠范围可以使用collapse()方法,这个方法接收一个 参数:布尔值,表示折叠到范围那一端.true折叠到起点,false表示折叠到终点.
range.collapse(true)
range.collapsed//true
范围比较
在有多个范围的情况下,可以使用compareBoundaryPoints()方法来确定范围是否有公共边界
该方法接收两个参数:
-
表示比较方式的常量:
-
Range.START_TO_START(0) 比较两个范围的起点
-
Range.START_TO_END(1) 比较第一个起点和第二个终点
-
Range.END_TO_END(2); 比较两个范围的终点
-
Range.END_TO_START(3) 比较第一个终点和第二个起点
-
要比较的范围
该方法对于第一个点在第二个比较的点之前返回-1
两个点相同返回0
第一个在第二个之后返回1
复制范围
cloneRange()方法可以复制范围.
清理
detach()把范围从文档中剥离出去.
range.detach()
range=null