DOM2和DOM3读书笔记
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DOM2和DOM3读书笔记相关的知识,希望对你有一定的参考价值。
二刷《高程》做的笔记,没什么技术含量就不发到首页啦!~
DOM1级主要定义html和XML文档底层结构,DOM2和DOM3在这个结构基础上引入更多交互能力,也支持更高级的XML特性。DOM2和DOM3级分为许多模块(模块之间具有某种关联),分别描述DOM的某个非常具体的子集。这些模块如下:
- DOM2级核心:在文档1级核心的基础上构建,为节点添加了更多方法和属性
- DOM2级视图:为文档定义了基于样式信息的不同视图
- DOM2级事件:说明了如何使用事件与DOM文档交互
- DOM2级样式:如何以编程方式访问和改变CSS样式信息
- DOM2级遍历和规范:引入了遍历DOM文档和选择其特定部分的新接口
- DOM2级HTML:在1级HTML基础上构建,添加更多属性,方法和新接口
- DOM3级XPath模块
- DOM3级加载与保存模块
DOM变化
DOM2和DOM3级的目的是扩展DOM API,以满足操作XML的所有需求,同时提供更好的错误处理和特性检测能力,从某种意义上讲实现这一目的很大程度意味着对命名空间的支持。
DOM2级核心没有引入新类型,只是在DOM1级基础上通过增加新方法和新属性来增强既有类型。
DOM3级核心同样增强即有类型,但也引入一些新类型。
DOM2级视图和DOM2级HTML也增强了DOM接口,提供了新的属性和方法。
- 针对XML命名空间的变化
- 其他方面的变化
(1)针对XML命名空间的变化:有了XML命名空间,不同XML文档的元素就可以混合在一起使用,共同构成格式良好的文档,而不用担心发生命名冲突。从技术上讲,HTML不支持XML命名空间,但XHTML支持XML命名空间。命名空间要使用 xmlns 特性来指定,XHTML的命名空间是http://www.w3.org/1999/xhtml,在任何格式良好的XHTML页面中,都应该将其包含在<html>元素中
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Example XHTML Pages</title> </head> <body> Hello! </body> </html>
该例中其中所有元素默认都被视为XHTML命名空间中的元素,想为XML命名空间创建前缀可以 xmlns:前缀 ="http://www/w3/org/1999/xhtml"
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"> <xhtml:head> <xhtml:title>Example XHTML page</xhtml:title> </xhtml:head> <xhtml:body xhtml:class="home"> Hello </xhtml:body> </xhtml:html>
这里为XHTML的命名空间定义了一个名为xhtml的前缀,并要求所有XHTML元素都以该前缀开头,在避免不同语言间的冲突时需要使用命名空间来限定特性。例如混合了XHTML和SVG语言的文档:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Example XHTML Page</title> </head> <body> <s:svg xmlns:s="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="width:100%;height:100%"> <s:rect x="0" y="0" width="100" height="100" style="fill:red"/> </s:svg> </body> </html>
该例中设置命名空间将<svg>标识为了与包含文档无关的元素,此时svg元素的所有子元素以及这些元素的所有特性都被认为属于http://www.w3.org/2000/svg命名空间,即使这个文档从技术上说是一个XHTML文档,但因为有了命名空间,其中的SVG代码也仍然是有效的。对于这类文档最有意思的是调用方法操作文档节点,例如创建一个元素这个元素属于哪个命名空间呢?DOM2级核心通过为大多数DOM1级方法提供特定于命名空间的版本解决了这个问题。
- Node类型的变化:
DOM2级中Node类型包含下列特定于命名空间的属性,均继承自Element.ptototype
localName:不带命名空间前缀的节点名称
namespace:命名空间URI或者(在未指定情况下是)null
prefix:命名空间前缀或者(在未指定情况下是)null
当节点使用命名空间前缀时其nodeName等于prefix:localName
DOM3级在此基础上引入了下列与命名空间有关的方法,均继承自Node.prototype
isDefaultNamespace(namespaceURI):在指定的namespaceURI是当前节点的默认命名空间的情况下返回true
lookupNamespaceURI(prefix):返回给定prefix的命名空间
lookupPrefix(namespaceURI):返回给定namespaceURI的前缀
在取得了一个节点,但不知道该节点与文档中其他元素之间的关系的情况下,这些方法很有用。 - Document类型的变化:DOM2级包含下列与命名空间有关的方法
createElementNS(namespaceURI, tagName):继承自Document.ptototype,使用给定的tagName创建一个属于命名空间namespaceURI的新元素。
createAttributeNS(namespaceURI, attributeName):继承自Document.ptototype,使用给定attributeName创建一个属于命名空间namespaceURI的新特性。
getElementsByTagNameNS(namespaceURI, tagName):继承自Document.prototype和Element.prototype,返回属于命名空间namespaceURI的tagName元素的HTMLcollection集合
只有在文档中存在两个或多个命名空间时,这些与命名空间有关的方法才是必须的。 - Element类型的变化:DOM2级核心中有关Element的变化主要涉及操作特性,继承自Element.prototype。
getAttributeNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的特性值
getAttributeNodeNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的特性节点。
getElementsByTagNameNS(namespaceURI, localName):返回属于命名空间namespaceURI的localName元素的HTMLCollection
hasAttributeNS(namespaceURI, localName):确定当前元素是否有一个名为localName的特性,而且该特性的命名空间为namespaceURI。DOM2级也增加了个hasAttribute()不用考虑命名空间
removeAttributeNS(namespaceURI, localName):删除属于命名空间namespaceURI且名为localName的特性
setAttribueNS(namespaceURI, qualifedName, value):设置属于命名空间namesapceURI且名为qualifedName的特性的值为value。
setAttributeNodeNS(attrNode):设置属于命名空间namespaceURI的特性节点
除了第一个参数之外,这些方法与DOM1级中相关方法的作用相同。 - NamedNodeMap类型的变化:特性是通过NamedNodeMap表示的,因此这些方法只针对特性节点使用,继承自NamedNodeMap.prototype。
getNamedItemNS(namespaceURI, localName):取得属于命名空间namespaceURI且名为localName的特性节点项。
removeNamedItemNS(namespaceURI, localName):移除属于命名空间namespaceURI且名为localName的项。
setNamedItemNS(node):添加node,这个特性节点已经事先指定了命名空间信息。
(2)其他方面的变化:DOM的其他部分在DOM2级核心上中也发生了些变化,这些变化与XML命名无关,而是更倾向于确保API的可靠性及完整性。
- DocumentType类型的变化:DocumentType.prototype新增3个属性,publicId,systemId,internalSubset。前两个属性表示文档类型声明中的两个信息段,这两个信息段在DOM1级中是没办法访问的。
internalSubset用于访问包含在文档类型声明中的额外定义,在XML中会常见。 - Document类型的变化:
(1).Document.prototype中唯一与命名空间空间无关的方法是importNode(),这个方法的用途是从一个文档中取得一个节点,然后将其导入另一个文档,使其成为这个文档结构的一部分。每个节点都有一个ownerDocument属性(继承自Node.prototype)表示所属文档。如果用appendChild()时候传入的节点属于不同的文档(ownerDocument属性的值不一样)则会导致错误(??Chrome下测试无错误)。但在调用importNode()传入不同文档的节点则会返回一个新节点,这个节点的所有权归当前文档所有。
importNode与cloneNode(继承自Node.prototype)方法非常相似,两个参数:要复制的节点,表示是否复制子节点的布尔值。返回的是原来节点的副本,但能够在当前文档中使用。这个方法在XML文档中常用。
框架里的document对象和主页面的document是不同的两个:
此时再查看HTML页面发现框架里table元素还在的。
(2).DOM2级视图模块添加了一个名为defaultView的属性(继承自Document.prototype),其中保存着一个指针,指向拥有给定文档的窗口(或框架)。除IE8之外的所有浏览器都支持defaultView,在所有IE中有一个等价的属性名parentWindow(Opera也支持这个属性),因此要确定文档归属窗口,兼容性代码为
var parentWindow = document.defaultView || document.parnetWindow;
(3).DOM2级核心还为document.implementation对象规定了两个新方法:createDocumentType,createDocument,均继承自DOMImplementation.prototype。
createDocumentType(doctypename, publicId, systemId):创建一个新的DocumentType节点,三个参数文档类型名称,publicId,systemId。如下面代码会创建一个新的HTML4.01 Strict文档类型var doctype = document.implementation.createDocumentType(\'html\', "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd");
由于既有文档的文档类型不能改变因此createDocumentType只在创建新文档时有用。
createDocument(namespaceURI, tagName, doctype):创建新文档,三个参数:namespaceURI,文档元素的标签名,新文档的文档类型。
(4).DOM2级HTML模块也为document.implementation增加了一个方法,叫createHTMLDocument(title),它的用途是创建一个完整的HTML文档,包括<html>,<head>,<title>,<body>元素。参数为文档的标题(放在title元素中的字符串),返回新的HTML文档实例(原型为HTMLDocumetn.prototype,因而继承该原型链上的所有属性和方法,包括title和body,均在Document.prototype上):
- Node类型的变化:
(1).唯一与命名空间无关的变化就是添加了isSupported(特性名, 版本号)方法,与DOM1级为document.implementation引入的isFeature()方法类似,用于确定当前节点具有什么能力。如果浏览器实现了相应特性而且能够基于给定节点执行该特性,isSupported就返回true,但是chrome下貌似没有这个属性啊...IE倒是有。由于不同实现在决定对什么特性返回true或false并不一致,这个方法同样也存在与hasFeature方法相同的问题,在确定某个特性是否可用时,最好还是使用能力检测。
(2).DOM3级引入了两个辅助比较节点的方法:isSameNode和isEqualNode,继承自Node.prototype。都接受一个节点参数,并在传入节点与引用节点相同或相等时返回返回true。相同是指两个节点引用的是同一个对象,相等是指两个节点是相同类型,具有相等的属性(nodeName,nodeValue等等)而且它们的attributes和childNodes属性也相等(相同位置包含相同的值)。
(3).Node.setUserData:已废弃 - 框架的变化:框架和内嵌框架分别用HTMLFrameElement和HTMLIFrameElement表示,它们在DOM2级中都有新属性为contentDocument(继承自HTMLFrameElement.prototype和HTMLIFrameElement.prototype)这个属性包含一个指针指向表示框架内容的文档对象(是HTMLDocument类型的实例,因此原型链继承属性和方法),在此之前无法直接通过元素取得取得这个文档对象(只能使用frames集合:window.frames[0].document)
IE8之前不支持框架集中的contentDocument属性,但支持contentWindow(继承自HTMLFrameElement和HTMLIFrameElement)的属性,该属性返回框架的window对象而这个window对象又有一个document属性,所有浏览器都支持contentWindow。兼容性代码为:
var iframe = document.getElementById(\'myIframe\'); var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
但要注意:访问框架或内嵌框架的文档对象要受到跨域安全策略的限制,如果某个框架中的页面来自其他域或不同域,或者使用了不同协议,那么要访问这个框架的文档对象就会导致错误。
样式
在HTML中定义样式方式有三种,通过link元素包含外部样式表文件,使用style元素定义嵌入样式表,使用style特性定义针对特定元素的样式。DOM2级样式模块围绕这三种应用样式的机制提供了一套API。
- 访问元素的样式:任何支持style特性的元素在javascript中都有一个对应的style属性(HTMLELement.prototype),这个style对象是CSSStyleDeclaration的实例,包含着通过HTML的style特性指定的所有样式信息,但不包含与外部样式表或嵌入式样式表经层叠而来的样式。在style特性中指定的任何css属性都将表现为都将表现为这个style对象的相应属性。对于使用短划线的CSS属性名,必须将其转化为驼峰大小写形式,才能通过JS访问。但也有例外,比如float,由于float是JS中的保留字,因此不能用作属性名,DOM2级样式规范规定样式对象上相应属性名应该是cssFloat,>=IE9和其他浏览器支持该属性名,所有IE也支持styleFloat属性名。
原型链继承关系为:document.body.style.__proto__->CSSStyleDeclaration.prototype->Object.prototype
在设置样式时,元素的外观会被自动更新,在标准模式下所有度量值都必须指定一个度量单位。混杂模式下可以将style.width设置为20,浏览器会假设它是“20px”,但在标准模式下,将style.width设置为20会导致被忽略,因为没有度量单位。
如果没有为元素设置style特性,那么style对象中可能会包含一些默认的值,但这些值并不能准确地反映该元素的样式信息。
之所以能手动改变元素样式值,是因为这样的,每个CSSStyleDeclaration的实例通过构造函数后都会拥有自己的这355(固定属性)+n(通过style特性设置,与ele.style.length值有关)个属性。
var i=0; var styles = document.body.style; for(k in styles){ if(styles.hasOwnProperty(k)){ console.log(k, styles[k]); i++; } } console.log("count", i); //控制台结果 0 overflow-x 1 overflow-y 2 font-size alignContent \'\' alignItems \'\' alignSelf \'\' alignmentBaseline \'\' all \'\' animation \'\' ...
overflow hidden
overflowWrap \'\'
overflowX hidden
overflowY hidden
... zoom \'\' cssFloat \'\' count 358
(1).DOM样式属性和方法:DOM2级样式规范还为style对象定义了一些属性和方法,它们均来自CSSStyleDeclaration.prototype,这些属性和方法在提供元素的style特性值得同时也可以修改样式。
cssText:读模式下,访问元素style特性中的css代码;写模式下赋给cssText的值会重写整个style特性的值。设置cssText是为元素应用多项变化最快捷的方式,因为可以一次性应用所有变化。
length:应用给元素的css属性的数量,就是定义在style特性中的样式数量,并且在style对象中以0,1,2...所以体现。其与item()属性配套使用以便迭代在元素中定义的css属性。在使用length和item()时候,style对象实际上就相当于一个集合,都可以使用方括号语法代替item()来取得给定位置的css属性名。
for(var i=0,len = document.body.style.length; i<len; i++){ console.log(document.body.style[i]); } // overflow-x overflow-y font-size
无论使用方括号语法还是item方法都可以取得被设置在style特性上的css属性名(是background-color不是backgroundColor)。然后可以在getPropertyValue()中使用取得的属性名进一步取得属性值。
var prop, value, i, len; for(i=0,len = document.body.style.length; i<len; i++){ prop = document.body.style[i]; value = document.body.style.getPropertyValue(prop); console.log(prop, value); } // overflow-x hidden overflow-y hidden font-size 16px
getPropertyPriority(propertyName):返回给定的属性使用了!important设置,则返回“important”,否则返回空字符串。
getPropertyValue(propertyName):返回给定属性的字符串值。
item(index):返回给定位置的CSS属性的名称,这个范围和length对应,即返回被设置在style特性中的样式,否则返回空字符串。
removeProperty(propertyName):从样式中删除给定属性,并即时反应在文档中,返回属性值。使用这个方法移除一个属性意味着将会为该属性应用默认样式(从其他样式表层叠而来),在不确定某个给定的CSS属性拥有什么默认值的情况下就可以使用这个方法,只要移除相应的属性就可以为元素应用默认样式。
setProperty(propertyName, value, priority):将给定属性设置为相应值,并加上优先权标志("important"或者一个空字符串)
注:这里的propertyName如果必要均用css短划线属性
(2).计算的样式:虽然style对象能提供支持style特性的任何元素的样式信息,但它不包含那些从其他样式表层叠而来的并影响到当前元素的样式信息。DOM2级样式增强了document.defaultView(defaultView继承自Document.prototype,document.default其实就是window),提供了getComputedStyle(ele, str/null)方法(该方法是window对象上的),两个参数要取得计算样式的元素,一个伪元素字符串(例如\':after\'),如果不需要伪元素信息,第二个参数可以是null。getComputedStyle()返回一个CSSStyleDeclaration对象(与style属性类型相同),其中包含当前元素的所有计算的样式。355+ n (计算的那些样式,索引从0开始)
这样看来,window.getComputedStyle()得到的CSSStyleDeclaration实例上的各个属性也是可重设置值的,但是并不然不清楚是为什么??明明实例上的writable是true的啊...知道的同学感谢告知...
这里要注意border边框属性可能会也可能不会返回样式表中实际的border规则,存在这个差别原因是不同浏览器解释综合属性(如border)的方式不同,因为设置这种属性实际上会涉及很多其他属性,在设置border时实际上是设置了四个边的边框宽度,颜色,样式属性(border-left-width,border-top-color,border-bottom-style等)。因此即使computedStyle.border不会在所有浏览器都返回相同的值,但computedStyle.borderLeftWidth会返回值。
Chrome:
FF:
IE:IE9+在使用getComputedStyle获取颜色时
IE5+通过currentStyle获取颜色:
<=IE8不支持getComputed方法,但在所有IE中每个具有style属性的元素都有一个currentStyle属性(继承自HTMLELement.prototype),这个属性是CSSStyleDeclaration的实例,包含当前元素全部计算样式。
注意:FF和Safari会将所有颜色转换成RGB格式,就连color属性设置的也会转为RGB,因此在使用getComputedStyle方法时最好多在几种浏览器下测试一下。
计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值的CSS属性都会表现在计算后的样式中。例如,所有浏览器中的visibility属性都有一个默认值,这个值因实现而异,默认情况下visibility值为visible,有的浏览器为“inherit”。如果你需要元素具有某个特定的默认值,应该手动在样式表中指定该值。 - 操作样式表:CSSStyleSheet类型表示的是样式表,包括通过<link>元素包含的样式表和在<style>元素中定义的样式表,这两个元素分别是HTMLLinkElement和HTMLStyleElement的实例。
CSSStyleSheet更加通用些,它只表示样式表,而不管这些样式表在HTML中是如何定义的。上述两个针对元素的类型允许修改HTML特性,但CSSStyleSheet对象则是一个只读接口(个别属性除外)。
原型链继承关系:document.styleSheets.__proto__->StyleSheetList.prototype->Object.prototype
应用于文档的所有样式表是通过document.styleSheets集合来表示的,通过这个集合的length属性(继承自StyleSheetList.prototype)可以获知文档中样式表的数量。通过[]或item方法(继承自StyleSheetList.prototype)可以访问每一个样式表。styleSheets属性继承自Document.prototype。
var sheet =null; for(var i=0, len = document.styleSheets.length; i<len; i++){ sheet = document.styleSheets[i]; console.log(sheet.href); }
可以看出,document.styleSheet的实例里的每一项都是CSSStyleSheet的实例
document.styleSheet[0].__proto__->CSSStyleSheet.prototype->StyleSheet.prototype->Object.prototype。
(1).StyleSheet.prototype上的:
除了disabled属性外,其他属性都是只读的
disabled:表示样式表是否被禁用的布尔值。这个属性是可读写的(get/set)的,将这个值设置为true可以禁用样式表。
href:如果样式表是通过<link>包含的,则是样式表的url,否则是null。(undefined/get)
media:当前样式表支持的所有媒体类型的集合。它的值为MediaList的实例,原型链关系为
document.styleSheets[0].media.__proto__-> MediaList.prototype->Object.prototype
如果集合是空列表表示适用于所有媒体。在<=IE8中,media是一个反映<link>和<style>元素media特性值的字符串。
ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在HTML中通过link或style元素引入的(在XML中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过@import导入的,则这个属性值为null。<=IE8不支持这个属性。
parentStyleSheet:在当前样式表是通过@import导入情况下,这个属性是一个指向导入它的样式表的指针。
title:ownerNode中title特性的值。
type:表示样式表类型的字符串,对css样式表来说这个字符串是“type/css”
(2).CSSStyleSheet.prototype上的:
cssRules:规则们,样式表中包含的样式规则的集合。其中每一项都是CSSStyleRule的实例,
原型链继承关系为:document.styleSheets[0].cssRules[0].__proto__->CSSStyleRule.prototype->CSSRule.protoype->Object.prototype
<=IE8不支持该属性,但有一个类似的rules属性。
ownerRule:如果样式表是通过@import导入的,这个属性就是一个指针,指向表示导入的规则,否则值为null,<=IE8不支持该属性。
deleteRule(index):删除cssRules集合中指定位置的规则,<=IE8不支持这个规则,但支持类似的removeRule方法。
insertRule(rule, index):向cssRules集合中指定的位置插入rule字符串。<=IE8不支持这个规则,但支持一个类似的addRule()方法。
不同浏览器的document.styleSheets返回的样式表也不同,所有浏览器都会包含<style>元素和rel特性被设置为‘stylesheet’的<link>元素引入的样式表。IE和Opera也包含rel特性被设置为“alternate stylesheet”的<link>元素引入的样式表。
也可以通过<link>或<style>元素取得CSSStyleSheet对象。DOM中规定了一个包含CSSStyleSheet对象的属性,为sheet(在HTMLLinkElement.prototype和HTMLStyleElement.prototype上)。除了<=IE8其他浏览器都支持这个属性,但所有的IE有个styleSheet属性,兼容性代码:
function getStyleSheet(ele){ return ele.sheet || ele.styleSheet; } var link = document.getElementsByTagName(\'link\')[0]; var sheet = getStyleSheet(link);
(1).css规则:CSSRule对象表示样式中每一条规则,实际上CSSRule是一个供其他多种类型继承的基类型,其中最常见的就是CSSStyleRule类型,表示样式信息。
CSSRule.prototype包含下列属性:
cssText:返回整条规则对应的文本。但浏览器对样式表的内部处理方式不同,返回的文本可能与样式表中实际的文本不一样。 Safari始终会将文本转换为全部小写,<=IE8不支持这个属性。
parentRule:如果当前规则是导入的规则,这个属性引用的就是导入规则;否则这个值为null。<=IE8不支持这个属性
parentStyleSheet:当前规则所属的样式表,<=IE8不支持这个属性。
type:表示规则类型的常量值,对于样式规则这个值是1,对于@import导入的规则这个值为3。<=IE8不支持这个属性。
CSSStyleRule.prototype上的:
selectorText:返回当前规则的选择符文本,由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样(Safari3之前的版本始终会将文本转换成全部小写)。<=IE8不支持这个属性。
style:一个CSSStyleDeclaration对象,可以通过它设置和取得规则中特定的样式值。
最常用的三个属性是cssText,selectorText,style。这里document.styleSheets[0].cssRules[0].cssText和document.getElementsByTagName(\'body\')[0].style.cssText不一样
前者包含选择符文本和围绕样式信息的花括号,后者只包含样式信息。大多数情况下仅使用style属性就可以满足所有操作样式规则的需求了。
通过样式规则也可修改样式,但以这种方式修改规则会影响页面中适用于该规则的所有元素。
(2).创建规则:DOM规定要向现有样式表中添加新规则,需要使用insertRule(rule, idx)方法,两个参数:规则文本和表示在哪里插入规则的索引,插入的规则将成为第idx条规则。IE早期版本支持addRule(selectorstr, styleinfo, idx)方法:三个参数,选择符文本,css样式信息,插入规则的位置(可选)。跨浏览器代码:
/* sheet: 样式表 selectorText:选择符文本 cssText:css样式信息 position:插入规则的位置 */ function insertRule(sheet, selectorText, cssText, position){ if(sheet.insertRule){ sheet.insertRule(selectorText + \'{\' + cssText + \'}\', position); }else if(sheet.addRule){ sheet.addRule(selectorText, cssText, position); } }
虽然这样可以动态添加规则但要添加的规则若是多还是建议使用动态加载样式表的技术。
(3).删除规则:从样式表中删除规则的方法,deleteRule(idx),参数为要删除规则的位置,IE支持一个removeRule(idx)。跨浏览器代码
function deleteRule(sheet, idx){ if(sheet.deleteRule){ sheet.deleteRule(idx); }else if(sheet.removeRule){ sheet.removeRule(idx); } }
考虑到删除规则可能会影响CSS层叠的效果,不推荐使用。
- 元素大小:下面几项并不属于DOM2级样式规范,但却与HTML样式息息相关。下面图片引用自(http://www.cnblogs.com/panjun-Donet/articles/1294033.html)
(1).偏移量:包括元素在屏幕上占用的所有可见空间(height/width+padding+滚动条+border),下面四个属性均继承自HTMLElement.prototype
offsetHeight:元素在垂直方向上占用的空间,以像素计。包括元素高度,可见的水平滚动条高度,上边框高度,下边框高度。
offsetWidth:元素在水平方向上占用空间大小,以像素计。
offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
包含元素的引用保存在offsetParent属性中(继承自HTMLElement.prototype)。offsetParent属性不一定与parentNode值相等。
offsetParent属性是一个只读属性,指向最近的定位元素,如果没有定位元素,则offsetParent为最近的table元素对象或根元素(标准模式下为HTML,严格模式下为body,不过我测试这个结果与MDN描述的有一些不相符,仅测试Chrome标准模式下仍是body...)。当元素的display设置为none,则其offsetParent返回null。webkit中如果元素为隐藏的(该元素或其祖先元素的style.display为none)或该元素的position被设置为fixed,offsetParent也返回null。IE9中如果该元素的position设置为fixed,则offsetParent返回null,设置为display:none无影响。
要向知道某元素在页面上(距离html)的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent相同属性相加,如此循环至根元素,就可得到一个基本准确的值(但是我觉得这样做还是忽略了border的宽度和高度,不过要是预先知道border可以后期加上border的距离)。
function getElementLeft(ele){ var actualLeft = ele.offsetLeft; var current = ele.offsetParent; while(current !== null ){ actualLeft += current.offsetLeft; current = current.offsetParent; } return actualLeft; } function getElementTop(ele){ var actualTop = ele.offsetTop; var current = ele.offsetParent; while(current !== null){ actualTop += current.offsetTop; current = current.offsetParent; } return actualTop; }
这两个函数利用offsetParent属性在DOM层次中逐级向上回溯,将每个层次中的偏移量属性合计到一块,对于简单的css页面这两个函数可以得到精确结果,对于表格和内嵌框架布局的页面,由于不同浏览器实现这些元素的方式不同因此得到的值不太精准。
(2).客户区大小:元素的内容及其内边距所占据空间大小,下面两属性继承自Element.prototype
所有这些偏移量属性都是只读的,而且每次访问它们都需要重新计算。因此该尽量避免重复访问这些属性。如果需要重复使用其中某些属性的值,可以将它们保存在局部变量中,以提高性能。
clientWidth:元素内容区宽度+左右内边距宽度
clientHeight:元素内容区高度+上下内边距高度
从字面看,客户区大小就是元素内部空间大小,因此滚动条占用的空间不计算在内。在确定浏览器视口大小的时候
function getView(){ if(document.compatMode == \'BackCompat\'){ //<IE7之前版本document.documentElement.clientWidth会返回0 return { width : document.body.clientWidth, height : document.body.clientHeight } }else{ return { width : document.documentElement.clientWidth, height : document.documentElement.clientHeight } }
}与偏移量相似,客户区的大小也是只读的,每次访问得重新计算。
(3).滚动大小:包含滚动内容的元素的大小。有些元素(例如html元素,我对高程上这块说的html有点异议,在Chrome下测试document.documentElement.scrollTop = 20;并没有什么卵用,但给body元素设置就可以),即使没有执行任何代码也能自动地添加滚动条,但另外一些元素,则需要通过css的overflow属性进行设置才能滚动。下面四个均继承自Element.protptype
scrollHeight:在没有滚动条情况下,元素内容总高度
scrollWidth:在没有滚动条情况下,元素内容总宽度
scrollLeft:被隐藏在内容区域左侧像素数,通过设置(设置的时候不带单位,带来单位的话Chrome默认是0)这个属性可以改变元素的滚动位置。
scrollTop:被隐藏在内容区域上方的像素数,通过设置这个属性可以改变元素滚动位置。
scrollWidth和scrollHeight主要用来确定元素内容的实际大小,通常认为html元素是在Web浏览器的视口中滚动的元素(IE6之前版本运行在混杂模式下是body元素。又经我测试...Chrome下是body元素,醉了)。在不包含滚动条的页面中,scrollWidth/scrollHeight和clientWidth/clientHeight之间的关系并不十分明确,这种情况下基于document.documentElement查看这些属性会在不同浏览器间发现一些不一致问题:
FF:两组属性值始终都是相等,但大小代表的是文档内容区域的实际尺寸,而非视口尺寸
Opera,Safari3.1+,Chrome中这两组属性有差别:scrollWidth/scrollHeight等于视口大小,clientWidth/clientHeight等于文档内容区域大小
IE(标准模式下)两组属性不想等:scrollWidth/scrollHeight等于文档内容区大小。clientWidth/clientHeight等于视口大小
在确定文档总高度时(包括基于视口的最小高度),必须取得scrollWidth/clientWidth和scrollheight/clientHeight最大值,才能保证在跨浏览器下得到精准结果。前提是html元素或body元素必须包含整个页面,像这样就不行
这样获得的scrollHeight为0,clientHeight为视口高度
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight); var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
就算这样我觉得也还是不精准,万一碰上像上图那样html元素高度为0,这获取文档高度就成clientHeight视口高度了,所以页面中一定要注意html元高度是否为0,高程上给的html这个元素太绝对了,我觉得应该是给包裹整个文档的元素才能由scrollHeight正确得到当前文档实际的高度。
(4).确定元素大小:多数浏览器提供了getBoundingClientRect()方法(继承自Element.prototype),这个方法返回一个ClientRect实例对象,这个对象包含四个属性:left,top,right,bottom,给出了元素在页面中相对于视口的位置。原型链继承关系为:document.body.getBoundingClientRect().__proto__->ClientRect.prototype->Object.prototype。
<=IE8认为文档左上角坐标(2,2)(又经我测试,IE8是(-2,-2),,IE7是(2,2),IE5是(0,0)哎尼玛...兼容性我想爆粗口),其他浏览器则认为(0,0),因此需要一开始检查一下位于(0, 0)处元素的位置。高程上给出一段代码来确定是否要对坐标进行调整(反正我是没咋看懂)
第一步检测属性是否有定义,如果没有就定义一个,需要创建一个临时元素将其位置设置为(0,0)然后再调用其getBoundingClientRect(),之所以减掉视口的scrollTop为了防止调用这个函数时窗口被滚动了。
第二步,在传入的元素上调用Element.prototype的getBoundingClientRect方法并基于新的公式创建一个对象。
function getBoundingClientRect(ele){ if(typeof arguments.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.getBoundingClientRect().top - scrollTop; document.body.removeChild(temp); temp = null; } var rect = ele.getBoundingClientRect(); var offset = arguments.callee.offset; return { left: rect.left + offset, right: rect.right + offset, top: rect.top + offset, bottom: rect.bottom + offset }; }
对于不支持getBoundingClientRect的浏览器可以通过其他手段取得相同信息,一般来说right和left的差值与offsetWidth值相等,bottom和top的差值与offsetHeight值相等。而且left和top属性大致等于前面的getElementLeft和getElementTop():综上创建下面这个跨浏览器函数(在某些情况下这个函数返回的值可能会有所不同,例如使用表格布局或使用滚动元素情况下,由于这里使用了argument.callee,所以这个方法不能在严格模式下使用)
function getBoundingClientRect(ele){ var scrollTop = document.documentElement.scrollTop; var scrollLeft = document.documentElement.scrollLeft; if(ele.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 = ele.getBoundingClientRect(); var offset = arguments.callee.offset; return { left: rect.left + offset, right: rect.right + offset, top: rect.top + offset, bottom: rect.bottom + offset }; }else { var actualLeft = getElementLeft(ele); var actualTop = getElementTop(ele); return { left : actualLeft - scrollLeft, right : actualLeft - scrollLeft + ele.offsetWidth, top: actualTop - scrollTop, bottom: actualTop- scrollTop +ele.offsetHeight } } }
遍历
DOM2级遍历和规范模块定义了两个用于辅助完成顺序遍历DOM结构的类型:NodeIterator和TreeWalker。这两个类型能够基于给定的起点对DOM结构执行深度优先的的遍历操作。<=IE8不支持DOM遍历,且在>IE8中 typeof NodeIterator == \'object\'; typeof TreeWalker == \'object\'; Chrome下是\'function\'。任何节点都可作为遍历的根节点,如果假设以html元素作为根节点,那么遍历的第一步就是访问head元素,第二部就是title节点...以document为根节点的遍历可以访问到文档中的全部节点,从document节点开始依序向前,访问的第一个节点是document,从文档最后节点开始,遍历可以反向移动到DOM树的顶端,NodeIterator和TreeWalker都以这种方式遍历
(1).NodeIterator:原型链继承关系为:document.createNodeIterator().__proto__->NodeIterator.prototypr->Object.prototype
NodeFilter.prototype->Object.prototype
可以通过document.createNodeIterator(root, whatToShow, filter, entityReferenceExpansion)方法创建NodeIterator的实例,四个参数
root:想要作为搜索起点的树中的节点
whatToShow:表示想要访问哪些节点的数字代码,是一个位掩码,通过应用一个或多个过滤器来确定要访问哪些节点。这个参数值以常量形式在NodeFilter类型中定义,如下:
NodeFilter.SHOW_ALL:显示所有类型节点,4294967295
NodeFilter.SHOW_ELEMENT:显示元素节点,1
NodeFilter.SHOW_ATTRIBUTE:显示特性节点,由于DOM结构原因,实际上不能使用这个值,2
NodeFilter.SHOW_TEXT:显示文本节点,4
NodeFilter.SHOW_CDATA_SECTION:显示CDATA节点,对HTML页面没有用,8
NodeFilter.SHOW_ENTITY_REFERENCE:显示实体引用节点,对HTML页面没有用,16
NodeFilter.SHOW_ENTITY:显示实体节点,对HTML页面没有用,32
NodeFilter.SHOW_PROCESSING_INSTRUCTION:显示处理指令节点,对HTML页面没有用,64
NodeFilter.SHOW_COMMENT:显示注释节点,128
NodeFilter.SHOW_DOCUMENT:显示文档节点,256
NodeFilter.SHOW_DOCUMENT_TYPE:显示文档类型节点,512
NodeFilter.SHOW_DOCUMENT_FRAGMENT:显示文档片段节点,对HTML页面没有用,1024
NodeFilter.SHOW_NOTATION:显示符号节点,对HTML页面没有用,2048
除了NodeFilter.SHOW_ALL外,可以使用按位或操作符组合多个选项,如 var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;
filter:是一个NodeFilter实例,或者一个表示应该接受还是拒绝某种特定节点的函数。如果不指定过滤器应该在第三个参数的位置传入null
1.每个NodeFilter实例对象上继承acceptNode方法,如果应该访问该节点该方法返回NodeFilter.FILTER_ACCEPT(1),如果不应该访问给定节点该方法返回NodeFIlter.FILTERT_SKIP(3),由于NodeFilter是一个抽象类型因此不能直接创建它的实例,在必要时只要创建一个包含acceptNode方法的对象,然后将这个对象传入createNodeIterator()中即可。
var filter = { accpectNode : function(node){ return node.tagName.toLowerCase() == \'p\'? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } }; var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);
2.这个参数也可以是一个与accpectNode方法类型的函数
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);
entityReferenceExpansion:布尔值,表示是否要扩展实体引用,这个参数在HTML页面中没有用,因为其中实体引用不能扩展。
创建一个能够访问所有类型节点的NodeIterator:
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false);
iterator继承了NodeIterator.prototype上的nextNode和previousNode,这两方法都是基于NodeIterator在DOM结构中的内部指针工作,所以DOM结构的变化会反映在遍历结果中。
nextNode方法先返回遍历器的内部指针所在的节点,然后会将指针移向下一个节点。所有成员遍历完成后,返回null。previousNode方法则是先将指针移向上一个节点,然后返回该节点。所以
var nodeIterator = document.createNodeIterator( document.body, NodeFilter.SHOW_ELEMENT ); var currentNode = nodeIterator.nextNode(); var previousNode = nodeIterator.previousNode(); currentNode === previousNode // true是相等的
在深度优先的DOM子树遍历中,nextNode方法用于前进一步(不是绝对的一步,而是找到filter里限定条件的那个节点为止,又可能走了好多步,所以是执行了多次过滤filter,每次过滤都会选择跳过还是接受)返回找到的那个节点,previousNode用于同理向后后退。在刚刚创建的NodeIterator对象中,有一个内部指针指向根节点,因此第一次调用nextNode会返回根节点,当遍历到DOM子树最后一个节点时nextNode返回null。previousNode工作机制类似。当遍历到DOM子树最后一个节点,且previousNode返回根节点后,再次调用它就会返回null。以下面为例:
<div id="div1"> <p><b>Hello </b> world</p> <ul> <li>List 1</li> <li>List 2</li> <li>List 3</li> </ul> </div>
//遍历div中所有元素 var div = document.getElementById(\'div1\'); var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, null, false); var node = iterator.nextNode(); // 返回根节点 while(node !== null ){ console.log(node.tagName); node = iterator.nextNode(); }
//返回li元素 var div = document.getElementById(\'div1\'); var filter = function(node){ return node.tagName.toLowerCase() == \'li\' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; }; var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false); var node = iterator.nextNode();//返回第一个li while(node !== null){ console.log(node.tagName); node = iterator.nextNode(); }
(2).TreeWalker:原型链继承关系为:document.createTreeWalker().__proto__->TreeWalker.prototype->Object.prototype
除了nextNode和previousNode外(和NodeIterator功能相同),还包括下列用于在不同方向上遍历DOM结构的方法
parentNode()
以上是关于DOM2和DOM3读书笔记的主要内容,如果未能解决你的问题,请参考以下文章
《JavaScript高级程序设计》Chapter 12 DOM2和DOM3