本篇内容
  • DOM2和DOM3变化
  • 访问、操作样式
  • 元素大小

DOM1级主要定义的是HTML和XML文档的底层结构。DOM2和DOM3级则在这个结构的基础上引入了更多的交互能力。也支持了更高级的XML特性。为此DOM2和DOM3级分为许多模块。

  • DOM2级核心(DOM Level 2 Core): 在1级核心基础上构建,为节点添加了更多方法和属性;
  • DOM2级视图(DOM Level 2 Views): 为文档定义了基于样式与信息的不同视图;
  • DOM2级事件(DOM Level 2 Events): 说明了如何使用事件与DOM文档交互;
  • DOM2级样式(DOM Level 2 Style): 定义了如何以编程方式来访问和改变CSS样式信息;
  • DOM2级遍历和范围(DOM Level 2 Traversal and Range): 引入了遍历DOM文档和选择其特定部分的新接口;
  • DOM2级HTML(DOM Level 2 HTML): 在1级HTML基础上构建,添加了更多属性、方法和新接口。

1 DOM变化

DOM2级核心没有引入新类型,它只是在DOM1级基础上通过增加新方法和新属性来增强了既有类型。DOM3级核心同样增强了既有类型,但也引入了一些新类型。

1.1 针对XML命名空间的变化

此篇不过多介绍。

“DOM2级核心” 通过为大多数 DOM1级方法提供特定于命名空间的版本解决了这个问题。

1.1.1 Node类型的变化

在 DOM2级中,Node类型包含下列特定于命名空间的属性。

  • localName:不带命名空间前缀的节点名称。
  • namespaceURI:命名空间URI或者(在未指定的情况下是) null。
  • prefix:命名空间前缀或者(在未指定的情况下是) null。

当节点使用了命名空间前缀时,其nodeName等于prefix+”:”+localName。

DOM3级在此基础上更进一步,又引入了下列与命名空间有关的方法。

  • isDefaultNamespace(namespaceURI):在指定的 namespaceURI是当前节点的默认命名空间的情况下返回true。
  • lookupNamespaceURI(prefix):返回给定prefix的命名空间。
  • lookupPrefix(namespaceURI):返回给定namespaceURI的前缀。

1.1.2 Document类型的变化

DOM2级中的Document类型也发生了变化,包含了下列与命名空间有关的方法。

  • createElementNS(namespaceURI, tagName):使用给定的tagName创建一个属于命名空间namespaceURI的新元素。
  • createAttributeNS(namespaceURI, attributeName):使用给定的attributeName创建一个属于命名空间namespaceURI的新特性。
  • getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间namespaceURI的tagName元素的NodeList。

使用这些方法时需要传入表示命名空间的URI(而不是命名空间前缀),如下面的例子所示。

1
2
3
4
5
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");

var att = docurnent.createAttributeNS("http://www.somewhere.com""random");

var elems = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "*");

1.1.3 Element类型的变化

“DOM2级核心” 中有关Element的变化,主要涉及操作特性。新增的方法如下。

  • getAttributeNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的特性。
  • getAttributeNodeNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的特性节点。
  • getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间namespaceURI的tagName元素的 NodeList。
  • hasAttributeNS(namespaceURI, localName):确定当前元素是否有一个名为localName的特性,而且该特性的命名空间是namespaceURI。注意,“DOM2级核心” 也增加了一个 hasAttribute()方法,用于不考虑命名空间的情况。
  • removeAttriubteNS(namespaceURI, localName):删除属于命名空间namespaceURI且名为localName的特性。
  • setAttributeNS(namespaceURI, qualifiedName, value):设置属于命名空间namespaceURI且名为qualifiedName的特性的值为value。
  • setAttributeNodeNS(attNode):设置属于命名空间namespaceURI的特性节点。

除了第一个参数之外,这些方法与DOM1级中相关方法的作用相同;第一个参数始终都是一个命名空间URI。

1.1.4 NamedNodeMap类型的变化

NamedNodeMap类型也新增了下列与命名空间有关的方法。

  • getNameditemNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的项。
  • removeNameditemNS(namespaceURI, localName):移除属于命名空间namespaceURI且名为localName的项。
  • setNameditemNS(node):添加node,这个节点已经率先指定了命名空间信息。

1.2 其他方面的变化

这些变化与XML命名空间元关,而是更倾向于确保API的可读性及完整性。

1.2.1 DocumentType类型的变化

DocumentType类型新增了3个属性:publicId、systemId和internalSubset。其中,前两个属性表示的是文档声明中的两个信息段,最后一个属性用于访问包含在文档类型声明中的额外定义。


1
2
<lOOCTYPE html PUBLIC”-//W3C//DTD XHTML 1. 0 Strict/ /EN
“http: I /www.w3.org/TR/xhtmll /DTD/xhtml 1-strict.dtd" [<!ELEMENT name(#PCDATA)>] >

访问document.doctype.internalSubset将得到”[<!ELEMENT name(#PCDATA)>"。

1.2.2 Document类型的变化

Document类型的变化中唯一与命名空间无关的方法是importNode()。这个方法的用途是从一个文档中取得一个节点,然后将其导人到另一个文挡,使其成为这个文档结构的一部分。需要注意的是,每个节点都有一个oownerDocument属性,表示所属的文挡。如果调用appendChild()时传入的节点属子不同的文挡(ownerDocument属性的值不一样),则会导致错误。但在调用importNode()时传人不同文挡的节点则会返回一个新节点,这个新节点的所有权归当前文档所有。

importNode()方法接收两个参数:复制的节点和一个表示是否复制子节点的布尔值。返回的结果是原来节点的副本,但能够在当前文档中使用。

1
2
var newNode = document.importNode(oldNode, true);   // 导入节点及其所有子节点
document.body.appendChild(newNode);

“DOM2级视图”模块添加了一个名为defaultView的属性,其中保存着一个指针,指向拥有给定文档的窗口(或框架)。除此之外,“视图”规范没有提供什么时候其他视图可用的信息, 因而这是唯一个新增的属性。除IE之外的所有浏览器都支持defaultView属性(IE中parentWindow等价)。


1
var parentWindow = document.defaultView || document.parentWindow;

两个新方法:createDocumentType()和createDocument()。前者用于创建一个新的DocumentType节点,接受3个参数: 文档类型名称、publicId、systemId。例如,下列代码会创建一个新的HTML 4.01 Strict文档类型。

1
var doctype = document.implementation.createDocumentType('html', '-//W3C//DTD HTML 4.01//EN', 'http://www.w3.org/TR/html4/strict.dtd');

由于既有文档的文档类型不能改变,因此createDocumentType()只在创建新文档时有用;创建新文挡时需要用到crcatcDocumcnt()方法。 这个方法接受3个参数:针对文档中元素的namespaceURI、 文档元素的标签名、新文挡的文档类型。下面这行代码将会创建一个空的新XML文档。

1
var doc = document.implementation.createDocument('', 'root', null);

这行代码会创建一个没有命名空间的新文档, 文档元素为<root>,而且没有指定文档类型。

“DOM2级HTML”模块也为document.implementation新增了一个方法——createHTMLDocument()。这个方法的用途是创建一个完整的HTML文档,包括<html><head><title><body>元素。这个方法只接受一个参数,即新创建文挡的标题(放在<title>元素中的字符串),返回新的HTML文档。


1
2
3
var htmldoc = document.implementation.createHTMLDocument('New html');

console.log(htmldoc.title); // 'New html'

通过调用createHTMLDocurnent()创建的这个文档,是HTMLDocument类型的实例,因而具有该类型的所有属性和方法,包括title和body属性。只有Opera和Safari支持这个方法。

1.2.3 Node类型的变化

Node类型中唯一与命名空间无关的变化,就是添加了isSupported()方法。

isSupported()方法用于确定当前节点具有什么能力。这个方法也接受相同的两个参数:特性名和特性版本号。如果浏览苦苦实现了相应特性,而且能够基于给定节点执行该特性, isSupported()就返回true。

1
2
3
if (document.body.isSupported('HTML', '2.0')) {
//
}

由于不同实现在决定对什么特性返回true或false时并不一致,这个方法同样也存在与hasFeature() 方法相同的问题。为此,我们建议在确定某个特性是否可用时,最好还是使用能力检测。

DOM3级引入了两个辅助比较节点的方法:isSameNode()和isEqualNode()。这两个方法都接受一个节点参数,并在传人节点与引用的节点相同戎相等时返回true。所谓相同,指的是两个节点引用的是同一个对象。所谓相等. 指的是两个节点是相同的类型,具有相等的属性(nodeName、nodeValue等等),而且它们的attributes和childNodes属性也相等(相同位置包含相同的值)。


1
2
3
4
5
6
7
8
var div1 = document.createElement('div');
div1.setAttribute('class', 'box');

var div2 = document.createElement('div');
div2.setAttribute('class', 'box');
alert(div1.isSameNode(div1)); // true
alert(div1.isEgualNode(div2)); // true
alert(div1.isSameNode(div2)); // false

DOM3级还针对为DOM节点添加额外数据引入了新方法。其中,setUserData()方法会将数据指定给节点,接受3个参数:要设置的键、实际的数据(可以是任何数据类型)和处理函数。 以下代码可以将数据指定给一个节点。

1
document.body.setUserData('name', 'micheal', function () {});

然后,使用getUserData()并传人相同的键,就可以取得该数据

1
var value = document.body.getUserData('name');

传入setUserData()中的处理函数会在带有数据的节点被复制、删除、重命名或引人一个文档时调用,因而你可以率先决定在上述操作发生时如何处理用户数据。
处理函数接受5个参数:表示操作类型的数值(1 表示复制,2 表示导入,3 表示删除, 4 表示重命名)、数据键、数据值、 源节点和目标节点。删除节点时,源节点是null;复制节点时,目标节点是null。在函数内部,你可以决定如何存储数据。

1
2
3
4
5
6
7
8
9
10
11
var div = document.createElement('div');
div.setUserDate('name', 'Micheal', function (operation, key, value, src, dest) {
if (operation == 1) {
dest.setUserData(key, value, function () {

})
}
});

var newDiv = div.cloneNode(true);
console.log(newDiv.getUserData('name')); // 'Micheal'

这里,先创建了一个<div>元素,然后又为它添加了一些数据(用户数据)。在使用cloneNode()复制这个元素时,就会调用处理函数,从而将数据自动复制到了副本节点。 结果在通过副本节点调用getUserData()时,就会返回与原始节点中包含的相同的值。

1.2.4 框架的变化

框架和内嵌框架分划用HTMLFrameElement和HTMLIFrameElement表示,它们在DOM2级中都有一个新属性,名叫contentDocument。这个属性包含一个指针,指向表示框架内容的文档对象。


1
2
3
4
var iframe = document.getElementById('test');

// IE8及以前无效
consoel.log(iframe.contentDocument);

由于contentDocument属性是Document类型的实例,因此可以像使用其他HTML文挡一样使用它,包括所有属性和方法。Opera、Firefox、Safari和Chrome支持这个属性。IE8之前不支持框架中的contentDocument属性,但支持一个名叫contentWindow的属性,该属性返回框架的window对象,而这个window对象又有-个document属性。

因此可以

1
iframe.contentDocument || iframe.contentWindow.document;

所有浏览器都支持contentWindow属性。

访问框架或内嵌框架的文档对象要受到跨域安全策略的限制。如果某个框架中的页面来自其他域或不同子域,或者使用了不同的协议,那么访问这个框架的文档对象就会导致错误。

URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

2 样式

在HTML中定义样式的方式有3种:通过<link/>元素包含外部样式表文件、使用<style>元素定义嵌入式样式,以及使用style特性定义针对特定元素的样式。“DOM2级样式”模块围绕这3种应用样式的机制提供了一套API。要确定浏览器是否支持DOM2级定义的css能力,可以使用下列代码。

1
2
var supportsDOM2CSS = document.implementation.hasFeature('css', '2.0'); 
var supportsDOM2CSS2 = document.implementation.hasFeature('css2', '2.0');

2.1 访问元素的样式

任何支持style特性的HTML元素在JavaScript中都有一个对应的style属性。这个style对象 是CSSStyleDeclaration的实例,包含着通过HTML的style特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。在style特性中指定的任何 css属性都将表现为这个style对象的相应属性。对于使用短划线(分隔不同的词汇,例如background-image)的css属性 名,必须将其转换成驼峰大小写形式,才能通过JavaScript来访问
常见属性如

css属性 JavaScript属性
background-image style.backgroundImage
color style.color
display style.display
font-family style.fontFamily

多数情况下,都可以通过简单地转换属性名的格式来实现转换。其中一个不能直接转换的css 属性是float。由于float是JavaScript中的保留字,因此不能用作属性名。“DOM2级样式”规定样式对象上相应的属性名应该是cssFloat; Firefox、Safari、Opera和Chrome都支持这个属性,而IE支持的则是styleFloat。

只要取得一个有效的 DOM 元素的引用,就可以随时使用JavaScript为其设置样式。

1
2
3
var test = document.getElementById('test');
test.style.color = '#f00';
test.style.fontSize = '30px'

在标准模式下,所有度量位都必须指定一个皮量单位。在混杂模式下,可以将style.width设置为’20’,浏览器会假设它是’20px’;但在标准模式下,将style.width设置为’20’会导致被忽略——因为没有度量单位。在实践中,最好始终都指定度量单位。

如果没有为元素设置style特性,那么style对象中可能会包含一些默认的值,但这些值并不能准确地反映该元素的样式信息。

2.1.1 DOM样式属性和方法

“DOM2级样式规范还为style对象定义了一些属性和方法。这些属性和方法在提供元素的style特性值的同时,也可以修改样式。下面列出了这些属性和方法。

  • cssText:通过它能够访问到style特性中的css代码。
  • length:应用给元素的css属性的数量。
  • parentRule:表示css信息的CSSRule对象。
  • getPropertyCSSValue(propertyName):返回包含给定属性值的CSSValue对象。
  • getPropertyPriority(propertyName):如果给定的属性使用了!important,则返回”important”;否则返回空字符串。
  • getPropertyValue(propertyName):返回给定属性的字符串值。
  • item(index):返回给定位置的css属性的名称。
  • rernoveProperty(propertyName):从样式中删除给定属性。
  • setProperty(propertyName, value, priority):将给定属性设置为相应的值,并加上优先权标识(”important”或空字符串)。

通过cssText属性可以访问style特性中的css代码。在读取模式下,cssText返回浏览器对style 特性中css代码的内部表示。在写入模式下,赋给cssText的值会重写整个style特性的值;也就是说,以前通过style特性指定的样式信息都将丢失。例如,如果通过style特性为元素设置了边框,然后再以不包含边框的规则重写cssText,那么就会抹去元素上的边框。

1
myDiv.style.cssText = "width: 25px; height: lOOpx; background-color: green";

设置cssText是为元素应用多项变化最快捷的方式,因为可以一次性地应用所有变化。

设计length属性的目的,就是将其与item()方法配套使用,以便选代在元素中定义的css属性。 在使用length和item()时,style对象实际上就相当于一个集合,都可以使用方括号谱法来代替item()采取得给定位置的css属性。

1
2
3
4
5
for (var i = 0, len = mydiv.style.length; i < len; i++) {
console.log(mydiv.style[i]); // 或mydiv.style.item(i);

console.log(mydiv.style.getPropertyValue(prop));
}

getPropertyValue()方法取得的始终都是css属性值的字符串表示。如果你需要更多信息,可
以使用getPropertyCSSValue()方法,它返回一个包含两个属性的CSSValue对象,这两个属性分别是:cssText和cssValueType。其中,cssText属性的值与getPropertyValue()返回的值相同,而cssValue可归属性则是一个数值常量,表示值的类型:

  • 0表示继承的值,
  • 1表示基本的值,
  • 2表示值列表,
  • 3表示自定义的值。

以下代码既输出 css属性值,也输出值的类型。

1
2
3
4
5
6
7
8
9
var prop, 
value,
i,
len,
for (i = 0, len = mydiv.style.length; i < len; i++) {
prop = mydiv.style[i];
value = mydiv.style.getPropertyCSSValue(prop);
console.log(value.cssText, value.cssValueType);
}

在实际开发中,getPropertyCSSValue()使用得比getPropertyValue()少得多。IE9+、Safari, iE 3+以及Chrome支持这个方法。Firefox7及之前版本也提供这个访问,但使用总返回null。
要从元素的样式中移除某个css属性.需要使用removeProperty()方法。使用这个方法移除一个属性,意味着将会为该属性应用默认的样式(从其他样式表经层叠而来)。

1
mydiv.style.removeProperty('border');

在不确定某个给定的 css属性拥有什么默认值的情况下,就可以使用这个方法。只要移除相应的属性,就可以为元素应用默认值。

本节讨论的属性都得到了IE9+、Opera9+、Safari、Firefox及Chrome支持。

2.1.2 计算的样式

虽然style对象能够提供支将style特性的任何元素的样式信息,但并不包含那些从其他样式表层叠而来并影响到当前元素的样式信息。“DOM2级样式”增强了document.defaultView,提供了 getComputedStyle()方法。这个方法接受两个参数:要取得计算样式的元索和一个伪元素字符串(例 *:after*)。如果不需要伪元素信息,第二个参数可以是null。getComputedStyle()方法返回一个CSSStyleDeclaration对象(与style属性的类型相同),其中包含当前元素的所有计算的样式。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html>
<head>
...
<style>
#mydiv{
width: 100px;
height: 300px;
background-color: #f00;
}
</style>
</head>
<body>
<div id="mydiv" style="background-color: red;border: 1px solid black;">
</div>
</body>
</html>

1
2
3
4
5
6
7
var mydiv = document.getElementById('mydiv');
computedStyle = document.defaultView.getComputedStyle(mydiv, null);

console.log(computedStyle.backgroundColor); // 'red'
console.log(computedStyle.width); // '100px'
console.log(computedStyle.height); // '300px'
console.log(computedStyle.border); // '1px solid black', 某些浏览器不一样

在设置border时,实际上是设置了四个边的边框宽度、颜色、样式属性(border-left-width、border-top-color、border-bottom-style等等)。因此,即使 computedStyle.border不会在所有浏览器中都返回值,但cornputedStyle.borderLeftWidth 则会返回值。

所有浏览器都支持这种功能,但表示方式可能有所区别。例如,Firefox和Safari会将所有颜色转换成RGB格式。

IE 不支持getComputedStyle()方法,但它有一种类似的概念。在IE中,每个具有style属性的元素还有一个currentStyle属性。这个属性是CSSStyleDeclaration的实例,包含当前元素全部计算后的样式。

1
2
3
4
var myDiv = document.getElementByid('mydiv');
var computedStyle = mydiv.currentStyle;

// ...

无论在哪个浏览器中,最重要的一条是要记住所有计算的样式都是只读的;不能修改计算后样式对象中的css属性。此外,计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值的css属性都会表现在计算后的样式中,不能指望某个css属性的默认值在不同浏览器中是相同的。

2.2 操作样式表

CSSStyleSheet类型表示的是样式表,包括通过<link>元素包含的样式表和在<style>元素中定义的样式表。
CSSStyleSheet继承自StyleSheet,后者可以作为一个基础接口来定义非css样式表。从styleSheet接口继承而来的属性如下。

  • disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为true可以禁用样式表。
  • href:如果样式表是通过<link>包含的,则是样式表 的URL;否则,是null。
  • media:当前样式表支持的所有媒体类型的集合。与所有DOM集合一样,这个集合也有一个length属性和一个item()方法。也可以使用方括号语法取得集合中特定的项。如果集合是空列表,表示样式表适用于所有媒体。在IE中,media是一个反映<link><style>元素media特性值的字符串。
  • ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在HTML中通过<link><style>引人的(在XML中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过@import导人的,则这个属性值为null。IE不支持这个属性。
  • parentStyleSheet:在当前样式表是通过@impart导入的情况下, 这个属性是一个指向导入它的样式表的指针。
  • title: ownerNode中title属性的值。
  • type:表示样式表类型的字符串。对 css样式表而言, 这个字符串是”type/css”。

除了disabled属性之外,其他属性都是只读的。

CSSStyleSheet类型还支持下列属性和方法:

  • cssRules:样式表中包含的样式规则的集合。IE不支持这个属性,但有一个类似的rules属性。
  • ownerRule:如果样式表是通过@import导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为null。 IE不支持这个属性。
  • deleteRule(index):删除cssRules集合中指定位置的规则。IE也不支持这个方法,但支持一个类似的removeRule()方法。
  • insertRule(rule, index):向cssRules集合rule指定的位置插入rule字符串。IE不支持这个方法,但支持一个类似的addRule()方法。

应用于文挡的所有样式表是通过document.styleSheets集合来表示的。通过这个集合的length属性可以获知文挡中样式表的数量,而通过方捂号语法或item()方法可以访问每一个样式表。

1
2
3
4
5
var sheet = null;
for (var i = 0, len = document.styleSheets.length; i < len; i++) {
sheet = document.styleSheets[i];
console.log(sheet.href);
}

以上代码可以输出文档中使用的每一个样式表的href属性(<style>元素包含的样式表没有href属性)。

不同浏览器的document.styleSheets返回的样式表也不同。所有浏览器都会包含<style>元素 和rel特性被设置为”stylesheet”的<link>元素引入的样式表。IE和Opera也包含rel特性被设置为 “alternate stylesheet”的<link>元素引入的样式表。
要想在不同浏览器中都能取得样式表对象,可以使用下列代码。

1
2
3
4
5
6
function getStyleSheet(element) {
return element.sheet || element.styleSheet;
}

var link = document.getElementsByTagName('link')[0];
var sheet = getStyleSheet(link);

这里,的getStylesheet()返回的样式表对象与document.styleSheets集合中的样式表对象相同。

2.2.1 CSS规则

CSSRule对象表示样式表中的每一条规则。CSSRule是一个供其他多种类型继承的基类型,其中最常见的就是CSSStyleRule类型,表示样式信息(其他规则还有@import、@font-face、 @page和@charset,但这些规则很少有必要通过脚本来访问)。CSSStyleRule对象包含下列属性。

  • cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能与样式表中实际的文本不一样。IE不支持
  • parentRule:如果当前规则是导人的规则, 这个属性引用的就是导人规则;否则,这个值为
    nullc。IE不支持这个属性。
  • parentStyleSheet:当前规则所属的样式表。IE不支持这个属性。
  • selectorText:返回当前规则的选择符文本。由于浏览器对样式表的内部处理方式不间,返回的文本可能会与样式表中实际的文本不一样(例如,Safari3之前的版本始终会将文本转换成全部小写)。在Firefox、Safari、Chrome和IE中这个属性是只读的。Opera允许修改selectorText。
  • style:一个CSSStyleDeclaration对象,可以通过它设置和取得规则中特定的样式值。
  • type:表示规则类型的常量值。对于样式规则,这个值是1。IE不支持这个属性。

其中三个最常用的属性是cssText、selectorText和style。cssText属性与style.cssText属性类似,但并不相同。前者包含选择符文本和围绕样式信息的花括号,后者只包含样式信息(类似于元素的style.cssText)。此外,cssText是只读的,而style.cssText也可以被重写。
大多数情况下, 仅使用style属性就可以满足所有操作样式规则的需求了。
如:

1
2
3
4
5
div.box{
background-color: blue;
width: 100px;
height: 200px;
}

假设这条规则位于页面中的第-个样式表中,而且这个样式表中只有这一条样式规则,那么通过下列代码可以取得这条规则的各种信息。

1
2
3
4
5
6
7
8
var sheet= document.styleSheets[O]; 
var rules = sheet.cssRules || sheet.rules;
var rule = rules[0];
console.log(rule.selectorText); // 'div.box'
console.log(rule.style.cssText); // 完整的css代码
console.log(rule.style.backgroundColor); // 'blue'
console.log(rule.style.width); // '100px'
console.log(rule.style.height); // '200px'

2.2.2 创建规则

DOM规定,要向现有样式表中添加新规则,需要使用insertRule()方法。这个方法接受两个参数:规则文本和表示在哪里插入规则的索引。

1
sheet.insertRule("body { background-color: silver", 0);

插入的规则将成为样式表中的第一条规则(插入到了位置0)——规则的次序在确定层叠之后应用到文档的规则时至关重要。Firefox、Safari、Opera和Chrome都支持insertRule()方法。

IE8及更早版本支持一个类似的方法,名叫adddRule(),也接收两必选参数:选择符文本和css样式信息;一个可选参数:插入规则的位置。
如:

1
sheet.addRule('body', 'background-color: silver', 0);

最多可以使用addRule()添加4095条样式规则。超出这个上限的调用将会导致错误。

要以跨浏览器的方式向样式表中插入规则,可以使用下面的函数。这个函数接受4个参数:要向其中添加规则的样式表以及与addRule()相同的3个参数。

1
2
3
4
5
6
7
function insertRule(sheet, selectorText, cssText, position) {
if (shheet.insertRule) {
sheet.insertRule(selectorText + '{' + cssText + '}', position);
} else if (sheet.addRule) {
sheet.addRule(selectorText, cssText, position);
}
}

2.2.3 删除规则

从样式表中删除规则的方法是deleteRule(),这个方法接受一个参数:要删除的规则的位置。

1
sheet.deleteRule(0);

IE支持的类似方法removeRule()。如

1
sheet.removeRule();

综合方法

1
2
3
4
5
6
7
function deleteRule(sheet, index) {
if (sheet.deleteRule) {
sheet.deleteRule(sheet, index);
} else if (sheet.removeRule) {
sheet.removeRule(sheet, index);
}
}

2.3 元素大小

元素大小

2.3.1 偏移量(offset dirmension)

包括元素在屏幕上占用的所有可见的空间。元素的可见大小由其高度、宽度决定,包指所有内边距、滚动条和边框大小(注意,不包括外边距)。通过下列4个属性可以取得元素的偏移量。

  • offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包指元素的高度、(可见的)
    水平滚动条的高度、上边框高度和下边框高度。
  • offsetWidth:元素在水平方向上占用的主间大小,以像素专计。包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度。
  • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
  • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。

offsetLeft和offsetTop属性与包含元素有关,包含元素的引用保存在offsetParent属性中。

要想知道某个元素在页面上的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;

while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}

return actualLeft;
}

function getElementTop(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;

while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}

return actualLeft;
}

所有这些偏移量属性都是只读的,而且每次访问它们都需要重新计算。 因此,应该尽量避免重复访问这些属性;如果需要重复使用其中某些属性的值, 可以将它们保存在局部交量中,以提高性能。

2.3.2 客户区大小(client dirmension)

指的是元素内容及其内边距所占据的空间大小。属性有两个:clientWidth和clientHeight。其中clientWidth属性是元素内容区宽度加
上左右内边距宽度;clientHeight属性是元素内容区高度加上上下内边距离度。

客户区大小就是元素内部的空间大小.因此滚动条占用的空间不计算在内。

要确定浏览器视口大小,可以使用document.documentElement或document.body(在IE7之前的版本中)的clientWidth和clientHeight。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getViewport () {
if (document.compatMode == 'BackCompat') {
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}

与偏移量相似,客户区大小也是只读的,也是每次访问都要重新计算的。

2.3.3 滚动大小(scroll dimension)

指的是包含滚动内容的元素的大小。
有些元素(例如<html>元素),即使没有执行任何代码也能自动地添加滚动条:但另外一些元素,则需要通过css的overflow属性进行设置才能滚动。

  • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
  • scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
  • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
  • scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。

scrollWidth和scrollHeight主要用于确定元素内容的实际大小。

在确定文挡的总高度时(包括基于视口的最小高度时),必须取得scrollWidth/clientWidth和scrollHeight/clientHeight中的最大值,才能保证在跨浏览器的环境下得到精确的结果。

1
2
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);

2.3.4 确定元素大小

IE、Firefox3+、Safari4+、Opera9.5及Chrome为每个元素都提供了一个getBoundingClientRect()方法。这个方法返回会一个矩形对象,包含4个属性:left、top、right和bottom。
浏览器的实现稍有不同。lE8及更早版本认为文挡的左上角坐标是(2, 2),而其他浏览器包括IE9则将(0, 0)作为起点坐标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function getBoundingClientRect(element) {
if (typeof argument.callee.offset != 'number') {
var scrollTop = document.documentElement.scrollTop;
var temp = document.createElement('div');

temp.style.cssText = 'position: absolute;left: 0;top: 0;';
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRece().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}

var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;

return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: react.bottom + offset
};
}

对于不支持getBoundingClientRect()的浏览器,可以通过其他手段取得相同的信息。一般来说,right和left的差值与offsetWidth的值相等,而bottom和top的差值与offsetHeight相等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function getBoundingClientRect(element) {
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;

if (element.getBoundingClientRect) {
if (typeof arguments.callee.offset != 'number') {
var temp = document.createElement('div');
temp.style.cssText = 'position:absolute;left:0;top:0;';
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}

var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;

return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bpttpm: rect.bpttpm + offset,
};
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);

return {
left: actualLeft + scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
};
}
}

(注:由于这里使用了arguments.callee,所以这个方法不能在严格模式下使用。)

温习:

  • 命名空间、Node、Document、Element的变化;
  • 访问元素的样式、计算操作样式;
  • 创建操作CSS规则;
  • 元素客户区大小、滚动大小

(完)