DOM 变化
DOM2 级和 3 级的目的在于扩展 DOM API, 以满足操作 XML 的所有需求, 同时提供更好的错误处理及特性检测能力. 从某种意义上讲, 实现这一目的的很大程度意味着对命名空间的支持.
- var supportsDOM2Core = document.implementation.hasFeature('Core', '2.0');
- var supportsDOM3Core = document.implementation.hasFeature('Core', '3.0');
- var supportsDOM2html = document.implementation.hasFeature('HTML', '2.0')
- var supportsDOM2Views = document.implementation.hasFeature('Views', '2.0');
- var supportsDOM2XML = document.implementation.hasFeature('XML', '2.0')
针对 XML 命名空间的变化
有了 XML 命名空间, 不同 XML 文档的元素就可以混合一起, 共同构成格式良好的文档, 而不必担心发生命名冲突.
命名空间要使用 xmlns 特性来指定.
- <html xmlns='http://www.w3.org/1999/xhtml'>
- <head>
- <title>Example XHTML page</title>
- </head>
- <body>
- Hello world!
- </body>
- </html>
Node 类型的变化
在 DOM2 级中, Node 类型包含下列特定于命名空间的属性.
localName: 不带命名空间前缀的节点名称
namespaceURI: 命名空间 URI 或者(在未指定的情况下是)null
prefix: 命名空间前缀或者(未指定的情况下是)null
其他方面的变化
DOM 的其他部分在'DOM2 级核心'中也发生了一些变化. 这些变化与 XML 命名空间无关, 而是更倾向于确保 API 的可靠性及完整性.
DocumentType 类型的变化
新增了 3 个属性: publicId,systemId 和 internalSubset.
Document 类型的变化
Document 类型的变化中唯一与命名空间无关的方法是 importNode().
需要注意的是, 每个节点都有一个 ownerDocument 属性, 表示所属的文档. 如果调用 appendChild()时传入的节点属于不同的文档 (ownerDocument 属性的值不一样), 则会导致错误. 但在调用 importNode() 时传入不同文档的节点则会返回一个新节点, 这个新节点的所有权归当前文档所有.
importNode()方法与 Element 的 cloneNode()方法非常类似, 它接受两个参数: 要复制的节点和一个表示是否复制子节点的布尔值.
返回的结果是原来节点的副本, 但能够在当前文件中使用.
- var newNode = document.importNode(oldNode, true); // 导入节点及其子节点
- document.body.appendChild(newNode);
Node 类型的变化
Node 类型中唯一与命名空间无关的变化, 就是添加了 isSupported()方法. 与 DOM1 级为 document.implementation 引入的 hasFeature()方法类似, isSupported()方法用于确定当前节点具有什么能力.
DOM3 级引入了两个辅助比较节点的方法: isSameNode()和 isEqualNode(). 这两个方法都接受一个节点参数, 并在传入节点与引用的节点相同或相等时返回 true.
所谓相同, 指的是两个节点引用的是同一个对象.
所谓相等, 指的是两个节点是相同的类型, 具有相等的属性(nodeName,nodeValue), 而且它们的 attributes 和 childNodes 属性也相等.
- var div1 = document.createElement('div');
- div1.setAttribute('class', 'box');
- var div2 = docuemnt.createElement('div');
- div2.setAttribute('class', 'box');
- div1.isSameNode(div1); // true
- div1.isSameNode(div2); // false
- div1.isEqualNode(div2); // true
框架的变化
略
样式
在 HTML 中定义样式的方式有 3 种: 通过 < link/>元素包含外部样式表文件, 使用 < style/>元素定义嵌入式样式, 以及使用 style 特性定义针对特定元素的样式.
DOM2 级样式模块围绕这 3 种应用样式的机制提供了一套 API.
访问元素的样式
任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性. 这个 style 对象是 CSSStyleDeclaration 的实例, 包含着通过 HTML 的 style 特性指定的所有样式信息, 但不包含与外部样式表或嵌入样式表经层叠而来的样式.
对于短划线的 CSS 属性名, 必须将其转换成驼峰大小写形式, 才能通过 JavaScript 访问.
- background-image -> style.backgroundImage
- font-family -> style.fontFamily
其中一个不能直接转换的 CSS 属性就是 float. 由于 float 是 JavaScript 中的保留字, 因此不能用作属性名. 规定对应的属性名是 cssFloat. 而 IE 中则是 styleFloat.
DOM 样式属性和方法
DOM2 级样式规范还为 style 对象定义了一些属性和方法. 这些属性和方法在提供元素的 style 特性值的同时, 也可以修改样式. 下面列出了这些属性和方法.
- cssText
- length
- parentRule
- getPropertyCSSValue(propertyName)
- getPropertyPriority(propertyName)
getPropertyValue(propertyName): 返回给定属性的字符串值
item(index): 返回给定位置的 CSS 属性的名称
removeProperty(propertyName): 从样式中删除给定属性.
setProperty(propertyName, value, priority)
设置 cssText 是为元素应用多项变化最快捷的方式:
myDiv.style.cssText = 'width: 25px; height: 100px'
迭代 CSS 属性:
- var prop, value, i, len;
- for (i = 0; len = myDiv.style.length; i <len; i++) {
- prop = myDiv.style[i]; // 或者 myDiv.style.item(i)
- value = myDiv.style.getPropertyValue(prop);
- console.log(prop + ':' + value)
- }
计算的样式
getComputedStyle()方法. 这个方法接受两个参数: 要取得计算样式的元素和一个伪元素字符串. 如果不需要伪元素信息, 第二个参数可以是 null.
- var myDiv = document.getElementById('myDiv');
- var computedStyle = myDiv.computedStyle // IE 环境下 myDiv.currentStyle
这个属性是 CSSStyleDeclaration 的实例.
操作样式表
CSSStyleSheet 对象是一套只读的接口(有一个属性例外). 使用下面的代码可以确定浏览器是否支持 DOM2 级样式表.
var supportDOM2StyleSheets = dcoument.implementation.hasFeature('StyleSheets', '2.0');
CSSStyleSheet 继承自 StyleSheet, 后者可以作为一个基础接口来定义非 CSS 样式表. 从 StyleSheet 接口继承而来的属性如下.
disabled: 表示样式表是否被禁用的布尔值.
href: 如果样式表是通过包含的, 则是样式表的 URL, 否则是 null
media: 当前样式表支持的所有媒体类型的集合
ownerNode: 指向拥有当前样式的节点的指针, 样式表可能是在 HTML 中通过 < link > 或 < style/>引入的. 如果当前样式表是其他样式表通过 @import 导入的, 则这个属性值为 null.IE 不支持这个属性.
parentStyleSheet: 在当前样式表通过 @import 导入的情况下, 这个属性是一个指向导入它的样式表的指针.
title: ownerNode 中 title 属性的值.
type: 表示样式表类型的字符串. 对 CSS 样式表而言, 这个字符串是'text/css'.
cssRules: 样式表中包含的样式规则的集合. IE 中类似的是 rules 属性.
ownerRule: 如果样式表是通过 @import 导入的, 这个属性就是一个指针, 指向表示导入的规则; 否则值为 null.
deleteRule(index): 删除 cssRules 集合中指定位置的规则.
insertRule(rule, index): 向 cssRules 集合中指定的位置插入 rule 字符串.
应用于文档的所有样式表是通过 document.styleSheets 集合来表示的.
- var sheet = null;
- for (var i = 0, len = document.styleSheets.length; i < len; i++) {
- sheet = document.styleSheets[i];
- console.log(sheet.href)
- }
元素大小
偏移量
offset dimension, 包括元素在屏幕上占用的所有可见的空间. 通过下面四个属性可以取得元素的偏移量.
- offsetHeight
- offsetWidth
- offsetLeft
- offsetTop
其中 offsetLeft 和 offsetTop 属性与包含元素有关, 包含元素的引用保存在 offsetParent 属性中. offsetParent 属性不一定与 parentNode 的值相等. 例如, 元素的 offsetParent 是作为其祖先元素的
元素, 因为
是在 DOM 层次中距
最近的一个具有大小的元素.
- function getElementOffsetLeft (element) {
- var currentLeft = element.offsetLeft;
- var parent = element.offsetParent;
- while (parent) {
- currentLeft += parent.offsetLeft;
- parent = parent.offsetParent;
- }
- return currentLeft;
- }
同理 // top
客户区大小 client dimension
是指元素内容及其内边距所占据的空间大小(content + padding).
clientWidth 属性是元素内容区宽度加上左右内边距宽度; clientHeight 属性是元素内容区高度加上上下内边距高度.
常见用法: 确定浏览器视口大小.
- function getViewport () {
- if (document.compatMode == 'BackCompat') {
- return {
- width: document.body.clientWidth,
- height: document.body.clientHeight
- }
- } else {
- return {
- width: document.documentElement.clentWidth,
- height: document.documentElement.clientHeight
- }
- }
- }
滚动大小(scroll dimension)
指的是包含滚动内容的元素的大小.
4 个与滚动大小相关的属性:
scrollHeight: 在没有滚动条的情况下, 元素内容的总高度.
scrollWidth: 在没有滚动条的情况下, 元素内容的总宽度.
scrollLeft: 被隐藏在内容区左侧的像素数.
scrollTop: 被隐藏在内容区域上方的像素数.
因为浏览器兼容性的问题, 在确定文档的总高度时, 必须取得 scrollWidth/clientWidth 和 scrollHeight/clientHeight 中的最大值, 才能保证在跨浏览器时得到精确的结果.
- var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
- var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
遍历
DOM2 级遍历和范围模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型: NodeIterator 和 TreeWalker
- var supportTraversals = document.implementation.hasFeature('Traversal', '2.0');
- var supportNodeIterators = (typeof document.createNodeIterator == 'function');
- var supportTreeWalker = (typeof document.createTreeWaler == 'function')
- NodeIterator
NodeIterator 类型是两者中比较简单的一个, 可以使用 document.createNodeIterator()方法创建它的新实例. 这个方法接受下列 4 个参数.
root: 想要作为搜索起点的树中的节点.
whatToShow: 表示要访问哪些节点的数字代码
filter: 是一个 NodeFilter 对象, 或者一个表示应该接受还是拒绝某种特定的的函数.
entityReferenceExpansion: 布尔值, 表示是否要扩展实体引用. 这个参数在 HTML 页面中没有用, 因为其中的实体引用不能扩展.
whatToShow 参数是一个位掩码, 通过应用一或多个 filter 来确定要访问哪些节点. 这个参数的值以常量形式在 NodeFilter 类型中定义.
NodeFilter.SHOW_ALL: 显示所有类型的节点.
NodeFilter.SHOW_ELEMENT: 显示元素节点.
NodeFilter.SHOW_ATTRIBUTE: 显示特性节点. 由于 DOM 结构原因, 实际上不能使用这个值.
NodeFilter.SHOW_TEXT: 显示文本节点.
NodeFilter.SHOW_CDATA_SECTION: 显示 CDATA 节点. 对 HTML 页面没有用.
NodeFilter.SHOW_ENTITY_REFERENCE: 显示实体引用节点. 对 HTML 页面没有用.
NodeFilter.SHOW_ENTITYE: 显示实体节点. 对 HTML 页面没有用.
NodeFilter.SHOW_PROCESSING_INSTRUCTION: 显示处理指令节点. 对 HTML 页面没有用.
NodeFilter.SHOW_COMMENT: 显示注释节点.
NodeFilter.SHOW_DOCUMENT: 显示文档节点.
NodeFilter.SHOW_DOCUMENT_TYPE: 显示文档类型节点.
NodeFilter.SHOW_DOCUMENT_FRAGMENT: 显示文档片段节点. 对 HTML 页面没有用.
NodeFilter.SHOW_NOTATION: 显示符号节点. 对 HTML 页面没有用.
除了 NodeFilter.SHOW_ALL 之外, 可以使用按位或操作符来组合多个选项:
var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;
可以通过 createNodeIterator()方法的 filter 参数来指定自定义的 NodeFilter 对象, 或者指定一个功能类似节点过滤器 (node filter) 的函数.
每个 NodeFilter 对象只有一个方法, 即 accept-Node(); 如果应该访问给定的节点, 该方法返回 NodeFilter.FILTER_ACCEPT, 如果不应该访问给定的节点, 该方法返回 NodeFilter.FILTER_SKIP. 由于 NodeFilter 是一个抽象的类型, 因此不能直接创建它的实例. 在必要时, 只要创建一个包含 acceptNode()方法的对象, 然后将这个对象传入 createNodeIterator()中即可.
- var filter = {
- acceptNode: function (node) {
- return node.tagName.toLowerCase() == 'p' ?
- NodeFilter.FILTER_ACCEPT :
- NodeFilter.FILTER_SKIP;
- }
- };
- var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);
第三个参数也可以是一个与 acceptNode()方法类似的函数, 如下所示.
- var filter = function (node) {
- return node.tagName.toLowerCase() == 'p' ?
- NodeFilter.FILTER_ACCEPT :
- NodeFilter.FILTER_SKIP;
- };
- var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);
如果不指定过滤器, 那么应该在第三个参数的位置上传入 null.
NodeIterator 类型的两个主要方法是 nextNode()和 previousNode(). 在刚刚创建的 NodeIterator 对象中, 有一个内部指针指向根节点, 因此第一次调用 nextNode()会返回根节点.
TreeWalker
TreeWalker 是 NodeIterator 的一个更高级的版本. 除了包括 nextNode()和 previousNode()在内的相同的功能之外, 这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法.
parentNode(): 遍历到当前节点的父节点.
firstChild(): 遍历到当前节点的第一个子节点.
lastChild(): 遍历到当前节点的最后一个子节点
nextSibling(): 遍历到当前节点的下一个同辈节点.
previousSibling(): 遍历到当前节点的上一个同辈节点.
范围
为了让开发人员更方便地控制页面,"DOM2 级遍历和范围" 模块定义了 "范围"(range)接口. 通过范围可以选择文档中的一个区域, 而不必考虑节点的界限.
DOM 中的范围
DOM2 级在 Document 类型中定义了 createRange()方法. 在兼容 DOM 的浏览器中, 这个方法属于 document 对象.
- var supportsRange = document.implementation.hasFeature('Range', '2.0');
- var supportsRange2 = (typeof document.createRange == 'function');
如果浏览器支持范围, 那么就可以使用 createRange()来创建 DOM 范围.
var range = document.createRange();
与节点类似, 新创建的范围也直接与创建它的文档关联在一起, 不能用于其他文档.
IE8 及更早版本中的范围
小结
DOM2 级规范定义了一些模块, 用于增强 DOM1 级
DOM2 级核心为不同的 DOM 类型引入了一些与 XML 命名空间有关的方法. 还定义了以编程方式创建 Document 实例的方法, 也支持创建 DocumentType 对象.
DOM2 级样式模块主要针对操作元素的样式信息而开发, 其特性简要总结如下:
每个元素都有一个关联的 style 对象, 乐意用来确定和修改行内的样式.
要确定某个元素的计算样式, 可以使用 getComputedStyle()方法.
IE 不支持 getCumputedStyle()方法, 但提供了 currentStyle()方法.
可以通过 document.styleSheets 集合访问样式表.
除 IE 之外的所有浏览器都支持针对样式表的这个接口, IE 也为几乎所有相应的 DOM 功能提供了自己的一套属性和方法.
DOM2 级遍历和范围模块提供了与 DOM 结构交互的不同方式:
遍历即使用 NodeIterator 或 TreeWalker 对 DOM 执行深度优先的遍历.
NodeIterator 是一个简单的接口, 只允许以一个节点的步幅前后移动. 而 treeWalker 在提供相同功能的同时, 还支持在 DOM 结构的各个方向上移动, 包括父节点, 同辈节点和子节点等方向.
范围是选择 DOM 结构中特定部分, 然后再执行相应操作的一种手段.
使用范围选区可以在删除文档中某些部分的同时, 保持文档结构的格式良好, 或者复制文档中的相应部分.
IE8 及更早版本不支持 DOM2 级遍历和范围模块, 但它提供了一个专有的文本范围对象, 可以用来完成简单的基于文本的范围操作. IE9 完全支持 DOM 遍历.
来源: https://juejin.im/post/5ad70ddf6fb9a028c22b4cbe