DOM
DOM(文档对象模型)是针对HTML和XML文档的一个API。描绘了一个层次化的节点树,允许开发人员添加,修改和删除页面的某一个部分。
-
节点层次
- DOM可以将任何HTML或XML文档描绘成一个由多层节点构成的结构。
- 节点分为几种不同的类型。每种类型分别表示文档中不同的信息或标记。
- 每个节点都有各自的特点,方法和数据。
- 每个节点之间和其他节点之间存在某种关系。
- 节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。
- 文档节点是每个文档的根节点。
-
<html>
元素称为文档元素。文档元素是文档的最外层元素,文档中的其他所有元素都是包含在文档元素中。 - 每个文档都只能有一个文档元素。在HTML中,文档元素始终都是
<html>
元素。在XML中,没有预定义的元素,则任何元素都有可能是文档元素。 - 每一段标记都可以用树中的一个节点表示。
-
Node类型
- 这个Node接口在JavaScript中是作为Node类型实现的;除了IE外,在其他所有浏览器中都可以访问到这个类型。
- JavaScript中的所有节点类型都是继承自Node类型,因此,所有节点类型都共享相同的基本属性和方法。
- 每个节点都有一个nodeType属性,用于表示节点类型。节点类型由在Node类型中定义的下列12个数据常量表示,任何节点必居其一。
常量名 常量值 节点类型 描述 Node.ELEMENT_NODE 1 Element 代表元素 Node.ATTRIBUTE_NODE 2 Attr 代表属性 Node.TEXT_NODE 3 Text 代表元素或属性中的文本内容 Node.CDATA_SECTION_NODE 4 CDATASection 代表文档中的 CDATA 部(不会由解析器解析的文本) Node.ENTITY_REFERENCE_NODE 5 EntityReference 代表实体引用 Node.ENTITY_NODE 6 Entity 代表实体 Node.PROCESSING_INSTRUCTION_NODE 7 ProcessingInstruction 代表处理指令 Node.COMMENT_NODE 8 Comment 代表注释 Node.DOCUMENT_NODE 9 Document 代表整个文档(DOM 树的根节点) Node.DOCUMENT_TYPE_NODE 10 DocumentType 向为文档定义的实体提供接口 Node.DOCUMENT_FRAGMENT_NODE 11 DocumentFragment 代表轻量级的 Document 对象(文档的某个部分) Node.NOTATION_NODE 12 Notation 代表 DTD 中声明的符号 - 由于IE没有公开Node类型的构造函数,所以为了确保跨浏览器兼容问题,最好是将nodeType属性与数字值进行比较。
- 对于元素节点,nodeName中保存的值始终是元素的标签名(大写),而nodeValue的值始终为null。
- 对于属性节点的 nodeName 是属性名称,nodeValue 属性包含属性值。
- 对于文本节点的 nodeName 永远是 #text,nodeValue 属性包含文本。
- 节点关系
- 每个节点都有一个childNodes属性,返回一个NodeList对象。该对象是一个类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。且该对象有length属性,但它不是Array的实例。该对象是基于DOM结构动态执行查询的结果,就是说DOM结构的变化会自动反映到该对象中。
- 每个节点都有一个parentNode属性,返回该节点的父节点。每个节点的previousSibling和nextSibling属性,分别返回该节点的上一个和下一个节点。
- 每个节点都有hasChildNodes()方法,这个方法在该节点包含一个或多个节点的情况下返回true。
- 每个节点都有一个ownerDocument属性,返回表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或多个文档中。
- 操作节点
- appendChild() 用于向childNodes列表的末尾添加节点。如果传入该方法中的节点是文档的一部分,那么该传入的节点就会被移动到新的位置。
- insertBefore() 用于向childNode列表中特定的位置添加节点。接收两个参数:要插入的节点和作为参照的节点,同时该方法被返回。插入的节点会被插入到参照节点的前一个位置。如果参照节点是null,那么效果等同于appendChild。
- replaceChild() 用于替换节点,接收两个参数:要插入的节点和替换的节点。要替换的节点将有这个方法返回并从文档树中剔除,同时由要插入的节点占据其位置。使用该方法插入一个节点时,该节点的所有关系指针都会从被他所替换的节点复制过来。替换的节点仍在文档中,但文档中没有其位置了。
- removeChild() 用于移除节点,接收一个参数:要移除的节点。被移除的节点成为该方法的返回值。移除的节点仍在文档中,但文档中没有其位置了。
- cloneNode() 用于创建调用该方法的节点的一个完全相同的副本。接收一个布尔值:表示是否执行深复制。在参数为true下,执行深复制,就是复制节点以及其整个子节点树,但是不会复制添加到DOM树节点中的JavaScript属性,如事件处理程序等(注意:IE中有一个BUG,它会复制事件处理程序,因此在复制之前最好移除事件处理程序);在参数为false下,执行浅复制,即只复制该节点,复制后的节点归文档所有,但并没有为他指定父节点,所以这个节点在文档中类似于“孤儿”。
-
Document类型
document对象是HTMLDocument的一个实例,表示整个HTML页面。document对象是window对象的一个属性,因此可以将其作为全局对象来访问。
-
Document节点有以下特征:
- nodeType的值为9
- nodeName的值为"#document"
- nodeValue的值为null
- parentNode的值为null
- ownerDocument的值为null
- 其子节点可能是一个DocumentType(最多一个),Element(最多一个),ProcessingInstruction或Comment
-
文档的子节点
- 访问document节点的html子节点内置的访问方法
- documentElement属性,该属性始终指向HTML页面中的
<html>
- 通过document.childNodes[0]列表来访问
- 通过document.firstChild来访问
- documentElement属性,该属性始终指向HTML页面中的
- document.body直接指向
<body>
元素 - document.doctype可以访问
<!DOCTYPE>
元素,不同浏览器有差异- IE8及之前的版本:如果存在文档类型声明,会将其错误的解释为一个注释并把它当成Comment节点;而document.doctype的值始终为null。
- IE9+以及Firefox:如果存在文档类型声明,则将其作为文档的第一个子节点;document.doctype是一个Document节点,也可以通过document.firstChild或document.childNodes[0]来访问同一个节点。
- Safari,chrome和Opera:如果存在文档类型声明,将其解析,但不作为文档的子节点。document.doctype是一个Document节点,但该节点不会出现在document.childNodes中。
-
<html>
元素外部的注释在不同浏览器中处理不同<!-- 第一条注释 --> <html></html> <!-- 第二条注释 -->
- ie8及之前的版本,Safari 3.1及更高的版本,opera和chrome 只会为第一条注释创建节点,不为第二条注释创建节点。因此第一条注释会成为document.childNodes中的第一个节点、
- ie9及更高的版本,将第一条注释创建为document.childNodes中的一个注释节点,也将第二条注释创建为document.childNodes中的注释节点。
- Firefox以及Safari 3.1之前的版本会完全忽略这两条注释。
- 访问document节点的html子节点内置的访问方法
-
文档信息
- document.title 包含
<title>
元素中的文本,会显示在浏览器窗口的标题栏或标签页上。修改title属性值也会改变<title>
元素。 - document.URL 包含页面完整的URL(即地址栏中显示的URL)。
- document.domain 只包含页面的域名。不能将这个属性值设置为URL中不包含的域。
- document.referrer 包含链接到当前页面的那个页面的URL。在没有页面来源的情况下,可能为空字符串。
- 解决跨域问题的一个方法:
- 由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而将每个页面的document.domain设置为相同的值,那么这些页面就可以互相访问对方包含的JavaScript对象。
- 例如,一个页面加载自
www.wrox.com
,其中包含一个内嵌框架,这个内嵌框架的页面加载自p2p.worx.com
。如果将document.domain值都设置为"wrox.com",那么他们之间就能相互通信了。
- document.title 包含
-
查找元素
- document.getElementById() 接收一个参数:要取得的元素ID。如果找到相应的元素则返回该元素,如果不存在则返回null。注意ID必须严格匹配,包括大小写。如果页面中有多个元素的ID相同,返回第一次出现的元素。
- document.getElementByTagName() 接收一个参数:要取得的元素标签名,返回的是包含零个或多个元素的NodesList。在HTML文档中,返回的是HTMLCollection对象,类似于NodeList对象。
- HTMLCollection对象中的项访问方式
- 使用方括号中使用数值语法或item()
- 使用nameItem()方法,该方法接收的参数为要查找的元素的name属性值
- 使用方括号中使用查找的元素的name属性值
- 如果传入参数为*,则表示要获取全部标签,按照它们出现的先后顺序出现。
- HTMLCollection对象中的项访问方式
- document.getElementByName() 接收一个参数:要取得元素的name属性值,返回所有具有该name属性值的元素。
-
特殊集合
- document.anchors 包含文档中所有带name特性的
<a>
元素。 - document.forms 包含文档中所有的
<form>
元素。 - document.images 包含文档中所有的
<img>
元素。 - document.links 包含文档中所有带href特性的
<a>
元素。
- document.anchors 包含文档中所有带name特性的
-
DOM一致性检测:检测浏览器实现了DOM的哪些部分,可以使用document.implementation.hasFeature()方法,该接收两个参数:要检测的DOM功能的名称和版本号。如果浏览器支持给定名称和版本的功能,则返回true,否则返回false。
-
文档写入
- document.write() 接收一个字符串参数,即要写入到输出流中的文本。可动态的向页面中加入内容
- document.writeln() 接收一个字符串参数,即要写入到输出流中的文本,但会在字符串的末尾加上一个换行符。可动态的向页面中加入内容
- document.open() 打开网页的输出流
- document.close() 关闭网页的输出流
-
Element类型
- 该类型用于表现XML或HTML元素,提供了对元素标签名,子节点及特性的访问。
- Element节点具有以下特征:
- nodeType的值为1
- nodeName的值为元素的标签名
- nodeValue的值为null
- parentNode的值为Document或Element
- 其子节点可能是Element,Text,Comment,ProcessiongInstruction,CADTASection或EntityReference
- 要访问元素的标签名,可以使用nodeName,也可以使用tagName
- HTML元素:任何元素的所有特性,都可以通过DOM元素本身的属性来直接访问。
- 所有HTML元素都由HTMLElement类型表示,不是直接通过这个联系,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的属性分别对应于每个HTML元素中都存在的下列标准特性。
- id,元素在文档中的唯一标识符
- title,有关元素的附加说明信息,一般通过工具提示条显示出来
- lang,元素的语言代码
- dir,语言的方向,值为"ltr"从左到右或"rtl"从右到左
- className,为元素指定的CSS类
- 所有HTML元素都由HTMLElement类型表示,不是直接通过这个联系,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的属性分别对应于每个HTML元素中都存在的下列标准特性。
- 取得特性:getAttribute() 接收一个参数:要取得的特性名
- 该方法也能获取到自定义特性
- 特性的名称是不区分大小写的,即"id"与"ID"是同一特性
- 当特性是style时,返回的是CSS文本
- 当特性是onclick这样的事件处理程序,返回相应代码的字符串
- 设置特性:setAttribute() 接收两个参数:要设置的特性名和值
- 如果特性已经存在,则会用指定的值替换现有的值;如果不存在,则新建该属性并赋值。
- 通过这个方法设置的特性名会被统一转换为小写形式。
- 删除特性:removeAttribute() 接收一个参数:即要删除特性的名称。会清除特性的值,也会将该特性从元素中彻底删除。
- attribute属性
- Element类型是使用attribute属性的唯一一个DOM节点类型。attribute属性中包含一个NameNodeMap,与NodeList类似。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NameNodeMap对象中。该对象有以下方法:
- getNameItem(name):返回nodeName属性等于name的节点
- removeNameItem(name):从列表中删除nodeName属性等于name的节点,并返回被删除特性的attr节点
- setNameItem(node):向列表中添加节点,以节点的nodeName属性为索引
- item(pos):返回位于数字pos位置处的节点
- attribute属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是特性的值。
- 对于attribute对象中的属性,不同浏览器返回的顺序不同。这些特性在XML或HTML代码中的先后顺序,不一定与它们出现在attribute对象中的顺序一致。
- Element类型是使用attribute属性的唯一一个DOM节点类型。attribute属性中包含一个NameNodeMap,与NodeList类似。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NameNodeMap对象中。该对象有以下方法:
- 创建元素:document.createElement() 接收一个参数:即要创建元素的标签名。这个标签名在HTML文档中不分大小写,但在XML文档中区分。
- 在IE中,可以在该方法中传入完整的元素标签,也可以包含属性。
- 一些问题可以通过传入完整的元素标签来解决:
- 不能设置动态创建的
<iframe>
元素的name特性 - 不能通过表单的reset()方法重设动态创建的
<input>
元素 - 动态创建的type特性值为"reset"的
<button>
元素重设不了表单 - 动态创建的一批name相同的单选按钮彼此毫无关系
- 不能设置动态创建的
-
Text类型
- 文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本内容可以包含转义后的HTML字符,但不能包含HTML代码。
- Text节点具有以下特征:
- nodeType的值为3
- nodeName的值为"#text"
- nodeValue的值为节点所包含的文本
- parentNode的值Element
- 不支持(没有)子节点
- 可以通过nodeValue属性或data属性来访问Text节点包含的文本,这两个属性中包含的值相同。
- 操作节点中的文本方法:
- appendData(text):将text添加到文本的末尾
- deleteData(offset,count):从offset指定的位置开始删除count个字符
- insertData(offset,text):在offset指定的位置插入text
- replaceData(offset,count,text):用text替换从offset指定的位置开始到offset+count为止处的文本
- spliceText(offset):从offset指定为止将当前文本节点分成两个文本节点
- substringData(offset,count):提取从offset指定的位置开始到offset+count为止处的字符串
- 文本节点还有length属性,保存节点中字符的数目。nodeValue.length和data.length保存相同的值。
- 在默认下,每个可以包含内容的元素最多只能有一个文本节点,而且必须有内容存在。
- 创建文本节点:document.createTextNode() 接收一个参数:要出入节点的文本或完整的HTML代码(含文本)。
- 如果两个文本节点是相邻的同胞节点,那么这两个节点的文本就会连接起来,中间不会有空格。
- 规范文本节点:如果一个父元素包含两个或多个文本节点,可以调用normalize()方法,将所有节点合并成一个节点,结果节点的nodeValue等于合并前每个文本节点的nodeValue值拼接的值。
- 分隔文本节点:spliceText(offset)将一个文本节点分割成两个文本节点,按照指定位置分隔nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新节点包含剩下的文本。该方法返回一个新文本节点,该节点与原节点的parentNode相同。
-
Comment类型
- 注释在DOM中是通过Comment类型表示的。
- Comment节点具有以下特征:
- nodeType的值为8
- nodeName的值为"#comment"
- nodeValue的值为注释的内容
- parentNode的值Element或Document
- 不支持(没有)子节点
- Comment类型与Text类型继承自相同的类,因此它也拥有除splitText之外的所有字符串操作方法。
- 可以通过nodeValue或data属性来取得注释的内容。
- 使用document.createComment()并为其传递注释文本可以创建注释节点。
-
CDATASection类型
- 该类型只针对XML文档,表示的是CDATA区域。
- 与Comment类似,拥有除了拥有除splitText之外的所有字符串操作方法。
- 具有以下特征:
- nodeType的值为4
- nodeName的值为"#cdata-section"
- nodeValue的值为CDATA区域中的内容
- parentNode的值Element或Document
- 不支持(没有)子节点
- 在真正的XML文档中,可以使用document.createCDataSection()来创建CDATA区域,只需传入节点的内容即可。
-
DocumentType类型
- 在web浏览器中并不常用,仅有Firefox,safari和opera支持。
- 包含着与文档的doctype有关的所有信息,具有以下特征:
- nodeType的值为10
- nodeName的值为doctype的名称
- nodeValue的值为null
- parentNode的值Document
- 不支持(没有)子节点
- 在DOM1级中,DocumentType对象不能动态创建,而只能通过解析文档代码的方式创建。支持它的浏览器会把DocumentType对象保存在document.doctype中。
- DOM1级描述了DocumentType对象的三个属性:
- name 表示文档类型的名称
- entities 由文档类型描述的实体的NameNodeMap对象
- notations 由文档类型描述的符号的NameNodeMap对象
- 通常,浏览器中的文档使用的都是HTML或XHTML文档类型,所以entities和notations一般为空列表。
-
DocumentFragment类型
- 在所有节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段是一种轻量级的文档,可以包含和控制节点,但不会像完整文档那样占用额外的资源。
- 具有以下特征:
- nodeType的值为11
- nodeName的值为"document-fragment"
- nodeValue的值为null
- parentNode的值null
- 其子节点可能是Element,Text,Comment,ProcessiongInstruction,CADTASection或EntityReference
- 创建文档片段,可以使用document.createDocumentFragment()方法,不需要传参。
- 文档片段继承了Node的所有方法,通常用于执行那些用于针对文档的DOM操作。
- 如果将文档中的节点添加到文档片段中,就会从文档树中删除该节点,也不会在浏览器中看到该节点。添加到文档片段中的新节点同样也不属于文档树。可以通过appendChild()或insertBefore()将文档片段中内容添加到文档树中。在将文档片段作为参数传递给这两个方法时,实际上只会将文档片段的所有子节点添加到相应位置上;文档片段永远不会成为文档树的一部分。
- 假设要为一个
<ul>
元素添加列表项,如果逐个添加列表项,将会导致浏览器反复渲染新信息。为避免这个问题,可以先将列表项添加到文档片段中,然后一次性添加到文档中。
-
Attr类型
- 元素的特性在DOM以Attr类型表示。在所有浏览器中(包含IE8),都可以访问Attr类型的构造函数和原型。
- 从技术角度看,特性就是存在于元素的attributes属性中的节点。特性节点有以下特征:
- nodeType的值为2
- nodeName的值为特性的名称
- nodeValue的值为特性的值
- parentNode的值null
- 在HTML中不支持(无)子节点
- 在XML中子节点可以是text或EntityReference
- Attr对象的三个属性:
- name 特性名称,与nodeName值相同
- value 特性的值,与nodeValue值相同
- specified 布尔值,区别特性是在代码中指定的还是默认的
- 使用document.createAttribute()并传入特性名称可以创建新的特性节点。
- 将新创建的特性节点添加到元素中,必须使用元素的setAttributeNode()方法。添加特性后,可以通过以下方法来访问特性:attributes属性,getAttributeNode()方法或getAttribute()方法。其中,attributes属性,getAttributeNode()方法都会返回对应特性的节点,getAttribute()方法只会返回特性的值。
动态脚本是指页面加载时不存在,但将来的某一时刻通过修改DOM动态添加的脚本。创建的两种方式:插入外部文件和直接插入JavaScript代码。
动态样式是指页面加载时不存在的样式,页面加载完成后动态添加到页面中的。加载外部样式文件的过程时异步的,也就是加载样式和执行JavaScript代码的过程没有固定的次序。
-
操作表格
- 为
<table>
添加的属性和方法如下
属性和方法 说明 caption 保存着对 <caption>
元素(如果有)的指针tBodies 是一个 <tbody>
元素的HTMLCollectiontFoot 保存着对 <tfoot>
元素(如果有)的指针tHead 保存着对 <thead>
元素(如果有)的指针rows 是一个表格中所有行的HTMLCollection createTHead 创建 <thead>
元素,并将其放入到表格中,返回引用createTFoot 创建 <tfoot>
元素,将其放到表格中,返回引用createCaption 创建 <caption>
元素,将其放到表格中,返回引用deleteTHead 删除 <thead>
元素deleteTFoot 删除 <tfoot>
元素deleteCaption 删除 <caption>
元素deleteRow(pos) 删除指定位置的行 insertRow(pos) 向rows集合中的指定位置插入一行 - 为
<tbody>
添加的属性和方法如下
属性和方法 说明 rows 保存着 <tbody>
元素中行的HTMLCollectiondeleteRow(pos) 删除指定位置的行 insertRow(pos) 向rows集合中的指定位置插入一行,返回对新插入行的引用 - 为
<tr>
添加的属性和方法如下
属性和方法 说明 cells 保存着 <tr>
元素中单元格的HTMLCollectiondeleteCell(pos) 删除指定位置的单元格 insertCell(pos) 向cells集合中的指定位置插入一个单元格,返回对新插入单元格的 - 为
-
使用NodeList
- NodeList,NameNodeMap以及HTMLCollection这三个集合都是动态的,即每当文档结构发生变化时,它们都会得到更新。因此,它们始终会保存着最新,最准确的信息。
- 本质上,所有NodeList对象都是在访问DOM文档时实时运行的查询。