DOM2 和 DOM3
DOM1(DOM Level 1)主要定义了 HTML 和 XML 文档的底层结构。DOM2(DOM Level 2)和DOM3(DOM Level 3)在这些结构之上加入更多交互能力,提供了更高级的 XML 特性。实际上,DOM2和 DOM3 是按照模块化的思路来制定标准的,每个模块之间有一定关联,但分别针对某个 DOM 子集。这些模式如下所示。
- DOM Core:在 DOM1 核心部分 的基础上,为节点增加方法和属性。
- DOM Views:定义基于样式信息的不同视图。
- DOM Events:定义通过事件实现 DOM 文档交互。
- DOM Style:定义以编程方式访问和修改 CSS 样式的接口。
- DOM Traversal and Range:新增遍历 DOM 文档及选择文档内容的接口。
- DOM HTML:在 DOM1 HTML 部分的基础上,增加属性、方法和新接口。
- DOM Mutation Observers:定义基于 DOM 变化触发回调的接口。这个模块是 DOM4 级模块,用于取代 Mutation Events。
DOM 的演进
DOM2 和 DOM3 Core 模块的目标是扩展 DOM API,满足 XML 的所有需求并提供更好的错误处理和特性检测。很大程度上,这意味着支持 XML 命名空间的概念。DOM2 Core 没有新增任何类型,仅仅在 DOM1 Core 基础上增加了一些方法和属性。DOM3 Core 则除了增强原有类型,也新增了一些新类型。
XML 命名空间
XML 命名空间可以实现在一个格式规范的文档中混用不同的 XML 语言,而不必担心元素命名冲突。严格来讲,XML 命名空间在 XHTML 中才支持,HTML 并不支持。因此,本节的 示例使用 XHTML
命名空间是使用 xmlns
指定的。XHTML 的命名空间是 http://www.w3.org/1999/xhtml
,应该包含在任何格式规范的 XHTML 页面的 <html>
元素中,如下所示:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
Hello world!
</body>
</html>
其他变化
样式
HTML 中的样式有 3 种定义方式:外部样式表(通过 <link>
元素)、文档样式表(使用 <style>
元素)和元素特定样式(使用 style
属性)。DOM2 Style 为这 3 种应用样式的机制都提供了 API。
存取元素样式
任何支持 style 属性的 HTML 元素在 JavaScript 中都会有一个对应的 style 属性。这个 style 属性是 CSSStyleDeclaration 类型的实例,其中包含通过 HTML style 属性为元素设置的所有样式信息,但不包含通过层叠机制从文档样式和外部样式中继承来的样式。HTML style 属性中的 CSS 属性在 JavaScript style 对象中都有对应的属性。因为 CSS 属性名使用连字符表示法(用连字符分隔两个单词,如 background-image),所以在 JavaScript 中这些属性必须转换为驼峰大小写形式(如backgroundImage)。
大多数属性名会这样直接转换过来。但有一个 CSS 属性名不能直接转换,它就是 float。因为 float 是 JavaScript 的保留字,所以不能用作属性名。DOM2 Style 规定它在 style 对象中对应的属性应该是 cssFloat。
任何时候,只要获得了有效 DOM 元素的引用,就可以通过 JavaScript 来设置样式。
let myDiv = document.getElementById('myDiv');
// 设置背景颜色
myDiv.style.backgroundColor = 'red';
// 修改大小
myDiv.style.width = '100px';
myDiv.style.height = '200px';
// 设置边框
myDiv.style.border = '1px solid black';
在标准模式下,所有尺寸都必须包含单位。在混杂模式下,可以把 style.width
设置为"20",相当于"20px"。如果是在标准模式下,把 style.width
设置为"20"会被忽略,因为没有单位。实践中,最好一直加上单位。
通过 style 属性设置的值也可以通过 style 对象获取。比如下面的 HTML:
<div id="myDiv" style="background-color: blue; width: 10px; height: 25px"></div>
如果元素上没有 style 属性,则 style 对象包含所有可能的 CSS 属性的空值。
1.DOM 样式属性和方法
DOM2 Style 规范也在 style 对象上定义了一些属性和方法。这些属性和方法提供了元素 style 属性的信息并支持修改,列举如下。
-
cssText
: 用于获取或设置元素的整个内联样式(即 style 属性中的所有 CSS 规则)。 -
parentRule
: 表示 CSS 信息的 CSSRule 对象。 -
item(index)
: 返回索引为 index 的 CSS 属性名。 -
length
: 应用给元素的 CSS 属性数量。 -
getPropertyValue(propertyName)
: 返回属性 propertyName 的字符串值。 -
getPropertyPriority(propertyName)
: 如果 CSS 属性 propertyName 使用了!important则返回"important",否则返回空字符串。 -
removeProperty(propertyName)
: 从样式中删除 CSS 属性 propertyName。 -
setProperty(propertyName, value, priority)
: 设置 CSS 属性 propertyName 的值为 value,priority 是"important"或空 字符串。
<div id="myDiv" style="background-color: red;width: 100px;height: 100px"></div>
<script>
let myDiv = document.getElementById('myDiv');
console.log(myDiv.style); // CSSStyleDeclaration 对象,包含所有的css样式空值
console.log(myDiv.style.cssText); // background-color: red; width: 100px; height: 100px;
// 对style内联样式进行的是覆盖,而不是写入
myDiv.style.cssText = 'background-color: blue;';
console.log(myDiv.style.cssText); // background-color: blue;
</script>
length
属性是跟 item()
方法一起配套迭代 CSS 属性用的。此时,style 对象实际上变成了一个集合,也可以用中括号代替 item()
取得相应位置的 CSS 属性名,使用 getPropertyValue()
以取得属性的值,如下所示:
// <div id="myDiv" style="background-color: red;width: 100px;height: 100px"></div>
let myDiv = document.getElementById('myDiv');
for (let i = 0, len = myDiv.style.length; i < len; i++) {
let cssName = myDiv.style[i];
let cssValue = myDiv.style.getPropertyValue(cssName);
console.log('属性名:', cssName, '属性值:', cssValue);
// 属性名: background-color 属性值: red
// 属性名: width 属性值: 100px
// 属性名: height 属性值: 100px
}
removeProperty()
方法用于从元素样式中删除指定的 CSS 属性。使用这个方法删除属性意味着会应用该属性的默认(从其他样式表层叠继承的)样式。
// <div id="myDiv" style="background-color: red;width: 100px;height: 100px"></div>
let myDiv = document.getElementById('myDiv');
myDiv.style.removeProperty('background-color');
2.计算样式
style 对象中包含支持 style 属性的元素为这个属性设置的样式信息,但不包含从其他样式表层叠继承的同样影响该元素的样式信息。DOM2 Style在 document.defaultView
上增加了 getComputedStyle()
方法。
这个方法接收两个参数:要取得计算样式的元素和伪元素字符串(如":after")。如果不需要查询伪元素,则第二个参数可以传 null
。getComputedStyle()
方法返回一个 CSSStyleDeclaration对象(与 style 属性的类型一样),包含元素的计算样式。
/**
* @param element 用于获取计算样式的Element
* @param pseudoElt 指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null)
*/
let style1 = window.getComputedStyle(element, [pseudoElt]);
// 两者是等价的
let style2 = document.defaultView.getComputedStyle(element, null);
返回的 style 是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。
<style>
#elem-container {
position: absolute;
left: 100px;
top: 200px;
height: 100px;
}
</style>
<div id="elem-container">Hello World!</div>
<script>
let elem = document.getElementById('elem-container');
let cssStyle1 = window.getComputedStyle(elem, null);
console.log(cssStyle1.getPropertyValue('height')); // 100px
let cssStyle2 = document.defaultView.getComputedStyle(elem, null);
console.log(cssStyle2.getPropertyValue('top')); // 200px
</script>
getComputedStyle
可以从伪元素拉取样式信息 (比如,::after, ::before, ::marker, ::line-marker)
<style>
h3::after {
content: 'rocks!';
}
</style>
<h3>generated content</h3>
<script>
let h3 = document.querySelector('h3'),
result = getComputedStyle(h3, '::after').content;
console.log(`the generated content is: ${result}`);
// the generated content is: "rocks!"
</script>
返回的对象与从元素的 style 属性返回的对象具有相同的类型;然而,两个对象具有不同的目的。从 getComputedStyle
返回的对象是只读的,可以用于检查元素的样式(包括由一个 <style>
元素或一个外部样式表设置的那些样式).
在许多在线的演示代码中,getComputedStyle
是通过 document.defaultView
对象来调用的。大部分情况下,这是不需要的,因为可以直接通过 window
对象调用。但有一种情况,你必需要使用 defaultView
, 那是在 firefox3.6 上访问子框架内的样式。
操作样式表
CSSStyleSheet 类型表示 CSS 样式表,包括使用 <link>
元素和通过 <style>
元素定义的样式表。注意,这两个元素本身分别是 HTMLLinkElement 和 HTMLStyleElement。CSSStyleSheet 类型是一个通用样式表类型,可以表示以任何方式在 HTML 中定义的样式表。另外,元素特定的类型允许修改HTML 属性,而 CSSStyleSheet 类型的实例则是一个只读对象(只有一个属性例外 disable
)。
CSSStyleSheet 接口代表一个 CSS 样式表,并允许检查和编辑样式表中的规则列表。它从父类型 StyleSheet 继承属性和方法。
-
disabled
: 表示样式表是否被禁用 (唯一可读写的属性)。 -
href
: 返回样式表的 URL,或者在未使用<link>
标签包含时返回null
。 -
media
: 包含样式表支持的媒体类型的 MediaList 对象。 -
ownerNode
: 指向拥有当前样式表的节点,可以是<link>
或<style>
元素。 -
title
: ownerNode 的 title 属性。 -
parentStyleSheet
: 指向导入当前样式表的样式表。 -
type
: 表示样式表的类型,对于 CSS 样式表是 "text/css"。 -
cssRules
: 包含当前样式表的所有样式规则的集合。 -
ownerRule
: 指向导入当前样式表的规则,如果不是通过 @import 导入的,则为null
。 -
insertRule(rule, index)
: 在指定位置插入样式规则。 -
deleteRule(index)
: 删除指定位置的样式规则。
document.styleSheets
表示文档中可用的样式表集合。这个集合的 length
属性保存着文档中样式表的数量,而每个样式表都可以使用中括号或 item()
方法获取。
for (const css of document.styleSheets) {
console.log(css); // CSSStyleSheet {}
}
console.log(document.styleSheets[0]); // CSSStyleSheet {}
console.log(document.styleSheets.item(0)); // CSSStyleSheet {}
console.log(document.styleSheets[0] === document.styleSheets.item(0)); // true
1.CSS规则
CSSRule 类型表示样式表中的一条规则。这个类型也是一个通用基类,很多类型都继承它,但其中最常用的是表示样式信息的 CSSStyleRule(其他 CSS 规则还有 @import
、@font-face
、@page
和 @charset
等,不过这些规则很少需要使用脚本来操作)。以下是 CSSStyleRule 对象上可用的属性。
-
cssText
: 返回整条规则的文本,可能与样式表中实际的文本不同(因为浏览器内部处理样式表的方式可能不同)。 -
parentRule
: 如果这条规则被其他规则(如@media)包含,则指向包含规则,否则为null
。 -
parentStyleSheet
: 包含当前规则的样式表。 -
selectorText
: 返回规则的选择符文本,可能与实际文本不同(因为浏览器内部处理样式表的方式可能不同)。 -
style
: 返回 CSSStyleDeclaration 对象,可设置和获取当前规则中的样式。 -
type
: 表示规则类型的数值常量,对于样式规则始终为 1。
在这些属性中,使用最多的是 cssText
、selectorText
和 style
。cssText
属性与 style.cssText
类似,不过并不完全一样。前者包含选择符文本和环绕样式声明的大括号,而后者则只包含样式声明(类似于元素上的 style.cssText
)。此外,cssText
是只读的,而 style.cssText
可以被重写。
多数情况下,使用 style 属性就可以实现操作样式规则的任务了。这个对象可以像每个元素上的 style 对象一样,用来读取或修改规则的样式。
<style>
div.box {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<script>
let sheet = document.styleSheets[0];
let rules = sheet.cssRules; // 取得规则集合
let rule = rules[0]; // 取得第一条规则
console.log(rule.selectorText); // div .box
// 两者之间的差异
console.log(rule.style.cssText); // background-color: blue; width: 100px; height: 200px;
console.log(rule.cssText); // div.box { background-color: blue; width: 100px; height: 200px; }
console.log(rule.style.backgroundColor); // "blue"
</script>
使用这些接口,可以像确定元素 style 对象中包含的样式一样,确定一条样式规则的样式信息。与元素的场景一样,也可以修改规则中的样式,如下所示:
let sheet = document.styleSheets[0];
let rules = sheet.cssRules;
let rule = rules[0]; // 取得第一条规则
rule.style.backgroundColor = 'red';
2.创建规则
CSSStyleSheet.insertRule()
方法用来给当前样式表插入新的样式规则(CSS rule),并且包含一些限制。
/**
* rule 一个包含了将要插入的规则的 DOMString。规则字符串必须包含的内容取决于它的类型:
* - rule-set 类型(普通带有选择器的规则),需要选择器和样式声明;
* - at-rule 类型(以 @ 开头的规则,如 @import, @media 等),需要 at-identifier 和规则内容。
* index 可选, 一个小于或等于 stylesheet.cssRules.length 的正整数,表示新插入的规则在CSSStyleSheet.cssRules 中的位置。默认值是 0。
*/
stylesheet.insertRule(rule [, index])
stylesheet.insertRule('body { background-color: silver }', 0); // 使用 DOM 方法
3.删除规则
CSSStyleSheet.deleteRule()
方法用来从当前样式表对象中删除指定的样式规则。
/**
* index: 是一个数字,用来指定删除样式规则的位置
*/
stylesheet.deleteRule(index);
与添加规则一样,删除规则并不是 Web 开发中常见的做法。考虑到可能影响 CSS 层叠的效果,删除规则时要慎重。
元素尺寸
本节介绍的属性和方法并不是 DOM2 Style 规范中定义的,但与 HTML 元素的样式有关。DOM 一直缺乏页面中元素实际尺寸的规定。IE 率先增加了一些属性,向开发者暴露元素的尺寸信息。这些属性现在已经得到所有主流浏览器支持。
1.偏移尺寸
第一组属性涉及偏移尺寸(offset dimensions),包含元素在屏幕上占用的所有视觉空间。元素在页面上的视觉空间由其高度和宽度决定,包括所有内边距、滚动条和边框(但不包含外边距)。以下 4 个属性用于取得元素的偏移尺寸。
-
offsetHeight
: 元素在垂直方向上占用的像素尺寸,包括它的高度、水平滚动条高度(如果可见)和上、下边框的高度。 -
offsetLeft
: 元素左边框外侧距离包含元素左边框内侧的像素数。 -
offsetTop
: 元素上边框外侧距离包含元素上边框内侧的像素数。 -
offsetWidth
: 元素在水平方向上占用的像素尺寸,包括它的宽度、垂直滚动条宽度(如果可见)和左、右边框的宽度。
其中,offsetLeft
和 offsetTop
是相对于包含元素的,包含元素保存在 offsetParent
属性中。offsetParent
不一定是 parentNode
。比如,<td>
元素的 offsetParent
是作为其祖先的 <table>
元素,因为 <table>
是节点层级中第一个提供尺寸的元素。
要确定一个元素在页面中的偏移量,可以把它的 offsetLeft 和 offsetTop 属性分别与 offsetParent的相同属性相加,一直加到根元素。
function getElementLeft(element) {
let actualLeft = element.offsetLeft;
let current = element.offsetParent;
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
function getElementTop(element) {
let actualTop = element.offsetTop;
let current = element.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
这两个函数使用 offsetParent 在 DOM 树中逐级上溯,将每一级的偏移属性相加,最终得到元素的实际偏移量。对于使用 CSS 布局的简单页面,这两个函数是很精确的。而对于使用表格和内嵌窗格的页面布局,它们返回的值会因浏览器不同而有所差异,因为浏览器实现这些元素的方式不同。一般来说,包含在 <div>
元素中所有元素都以 <body>
为其 offsetParent
,因此 getElementLeft()
和 getElementTop()
返回的值与 offsetLeft
和 offsetTop
返回的值相同。
/**
* @param element: 元素节点
* @param direction: offsetTop | offsetLeft
*/
function getElementOffset(element, direction) {
let actualLeft = element[direction];
let current = element.offsetParent;
while (current !== null) {
actualLeft += current[direction];
current = current.offsetParent;
}
return actualLeft;
}
2.客户端尺寸
元素的客户端尺寸(client dimensions)包含元素内容及其内边距所占用的空间。客户端尺寸只有两个相关属性:clientWidth
和 clientHeight
。其中,clientWidth
是内容区宽度加左、右内边距宽度,clientHeight
是内容区高度加上、下内边距高度。
客户端尺寸实际上就是元素内部的空间,因此不包含滚动条占用的空间。这两个属性最常用于确定浏览器视口尺寸,即检测 document.documentElement
的 clientWidth
和 clientHeight
。这两个属性表示视口( <html>
或 <body>
元素)的尺寸。