Javascript之DOM
1 DOM
DOM重点:
- 理解包含不同层次节点的DOM
- 使用不同的节点类型
- 克服浏览器兼容性问题及各种陷阱
DOM:文档对象模型[Document Object Model],提供访问和操作页面网页内容的方法和接口,DOM可以把HTML看做是文档树,通过DOM提供的API可以把树上的节点进行操作。DOM是针对HTML和XML文档的一个API(应用程序编程接口)。
- DOM1级,有两个模块组成:DOM核心(DOM Core)和DOM HTML。DOM HTML是在DOM核心上扩展,增加了HTML的对象和方法。
- DOM2级,增加模块:DOM视图、DOM事件、DOM样式、DOM遍历和范围。
- DOM3,引入了以统一方式加载和保存文档的方法,在DOM加载和保存模块中定义、新增验证文档的方法、DOM核心进行扩展,支持XML1.0规范,涉及XML Infoset、XPath、XML Base。
1.1 节点层次
DOM:将任何XML或HTML文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及标记。每一段标记都可以通过树中的一个节点来表示:HTML元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示,总共有12种节点类型,这些类型都继承自一个基类型。
1 Node类型
DOM1定义了一个Node接口,该接口将由DOM中的所有节点类型实现。
//节点类型由在Node类中定义的下列12个数值常量来表示 Node.ELEMENT_NODE(1); // 元素节点 Node.ATTTRIBUTE_NODE(2); // 属性节点 Node.TEXT_NODE(3); // 节点 Node.CDATA_SECTION_NODE(4);// 节点 Node.ENTITY_REFERENCE_NODE(5);// 节点 Node.ENTITY_NODE(6);// 节点 Node.PROCESSING_INSTRUCTION_NODE(7);// 节点 Node.COMMENT_NODE(8);// 节点 Node.DOCUMENT_NODE(9);// 节点 Node.DOCUMENT_TYPE_NODE(10);// 节点 Node.DOCUMENT_FRAGMENT_NODE(11);// 节点 Node.NOTATION_NODE(12);// 节点 eg1: //IE不兼容,确定节点类型 if (somenode.nodeType == Node.ELEMENT_NODE){ alert("Node is an element"); } eg2: //适用于所有浏览器,确定节点类型 if (somenode.nodeType == 1){ alert("Node is an element"); }
//1.nodeName和nodeValue属性,完全取决于节点的类型 if (somenode.nodeType == 1){ value = somenode.nodeName;//判断是否为元素[元素节点:保存的是元素始终是标签名],nodeValue属性始终为null。 }
A Node之间的节点关系
文档中所有的节点之间都存在不同的关系。
每个节点都有一个childNodes属性,保存一个NodeList对象[NodeList是一种类型数组对象,用于保存一组有序的节点,通过位置进行访问]。
每个节点还有一个parentNode顺序,该属性指向文档树中的父节点。
所有节点都有最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点。
//访问NodeList中的节点,可以通过方括号,也可以使用item()方法。 var firstChild = someNode.childNodes[0]; var secondChild = someNode.childNodes.item(1); var secondChild = someNode.childNodes.length;//节点数量 //每个节点都有一个parentNode属性,指向文档树中的父节点。包含在childNodes列表中的所有节点都具有相同的父节点,包含在列表中的每个节点相互之间都是同胞节点。 //previousSibling[列表中第一个节点]、nextSibling[列表中最后一个节点]属性,可以访问同一列表中的其他节点。previousSibling=null,nextSibling=null if (someNode.nextSibling === null){ alert("Last node in the parent's childNodes list."); }else if (someNode.previousSibling=== null){ alert("First node in the parent's childNodes list."); }
B Node之间的操作节点
关系指针都是只读的,DOM提供了一些操作节点的方法。
- appendChild()节点:插入节点,用于向ChildNodes列表的末尾添加一个节点。 添加节点后,childNodes的新增节点、父节点、以前的最后一个子节点的关系指针都会相应地得到更新。更新完成后,appendChild()返回新增的节点。
- insertBefore()节点:插入节点,接收2个参数[要插入的节点、作为参照的节点]。把节点放在childNodes列表中某个特定的位置上,而不是放在末尾。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。如果参照节点为null,则与appendChild()执行相同的操作。
- replaceChild()节点:替换节点,接收2个参数[要插入的节点、要替换的节点]。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位子。
- removeChild()节点:移除节点,接收一个参数[要移除的节点],被移除的节点将成为方法的返回值。
- cloneNode():用于创建调用这个方法的节点的一个完全相同的副本。接收一个布尔值参数,表示是否执行深复制。[true为深复制,false为浅复制]。
- normalize():处理文档树中的文本节点。
深复制:复制节点及其整个子节点数。
浅复制:只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。
eg1: //DOM树看成是一系列指针连接起来的,但任何DOM节点也不能同时出现在文档中的多个位置上。 //appendChild(),用于向childNodes列表的末尾添加一个节点。 var returnedNode = someNode.appendChild(newNode); alert(returnedNode == newNode); //true alert(someNode.lastChild == newNode); //true //appendChild(),从原来的位置转移到新位置。 //appendChild()传入了父节点的第一个节点,该节点就会成为父节点的最后一个子节点。 var returnedNode = someNode.appendChild(someNode.firstChild); alert(returnedNode == someNode.firstChild); //false alert(someNode.lastChild == someNode.lastChild); //true eg2: //someNode有多个子节点,放在特定的位置上,不是末尾,使用insertBefore()方法。 //insertBefore()方法,接受2个参数:要插入的节点和作为参照的节点。 //insertBefore(),插入后成为最后一个子节点 returnedNode = someNode.insertBefore(newNode,null); alert(newNode== someNode.lastChild); //true //insertBefore(),插入后成为第一个子节点 returnedNode = someNode.insertBefore(newNode,someNode.firstChild); alert(returnedNode == newNode); //true alert(newNode== someNode.firstChild); //true //insertBefore(),插入到最后一个子节点前面 returnedNode = someNode.insertBefore(newNode,someNode.lastChild); alert(newNode== someNode.childNodes[someNode.childNodes.length-2]); //true eg3: //replaceChild()接收2个参数:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中移出,同时由要插入的节点占据其位置。 //replaceChild(),替换第一个子节点 var returnedNode = someNode.replaceChild(newNode,someNode.firstChild); //replaceChild(),替换最后一个子节点 var returnedNode = someNode.replaceChild(newNode,someNode.lastChild); eg4: //removeChild(),移除节点,接收一个参数:移除的节点。 //removeChild(),移除第一个子节点 var formerFirstChild= someNode.removeChild(newNode,someNode.firstChild); //replaceChild(),移除最后一个子节点 var formerLastChild= someNode.removeChild(newNode,someNode.lastChild); eg5: //cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本。true深复制,false浅复制。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。 <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> //深复制 var deepList= MyList.cloneNode(true); alert(deepList.childNodes.length); //7 //浅复制 var shallowList= MyList.cloneNode(true); alert(shallowList.childNodes.length); //0
2 Document类型
Document:文档,document对象表示整个HTML页面,同时是window对象的一个属性,可以作为全局对象来访问。
Document特征:
- nodeType:值为9;
- nodeName:值为"#document";
- nodeValue:值为null;
- parentNode:值为null;
- ownerDocument:值为null;
- 其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、processingInstruction或comment.
//1.文档的子节点:html、body、DocumentType、注释 document对象还有一个body属性,直接指向<body>元素。 eg: //浏览器解析后,文档中包含一个子节点:<html>元素 <!--第一条注释 --> <html> <body> </body> </html> //2.文档信息,可以通过documentElement或childNodes列表来访问这个元素。 //documentElement、childNodes[0]、firstChild的值相同,都指向<html>元素。 var html=document.documentElement; //取得对<html>的引用 alert(html === document.childNodes[0] ); //true alert(html === document.firstChild); //true var body=document.body; //取得对<body>的引用 var doctype=document.doctype; //取得对<!DOCTYPE>的引用 var originalTitle=document.title; //设置文档标题 var originalTitle=document.title; //设置文档标题 var url=document.URL; //取得完整的URL var domain=document.domain; //取得域名 var referrer=document.referrer; //取得来源页面的URL
//3.查找元素,取得元素的操作可以使用document对象的几个方法来完成。 //getElementById()、getElementsByTagName() //getElementById():接收一个参数:要取得的元素的ID。结果:返回该元素或null。 eg: <div id="myDiv">Some text</div> var div = document.getElementById("myDiv"); //取得<div>元素的引用
3 Element类型
Element类型用于表现XML或HTML元素,提供了对元素标签名,子节点及特性的访问.
Element 节点具有以下特征:
- nodeType 的值为 1;
- nodeName 的值为元素的标签名;
- nodeValue 的值为 null;
- parentNode 可能是 Document 或 Element;
- 其子节点可能是 Element、 Text、 Comment、 ProcessingInstruction、 CDATASection 或EntityReference.
eg://元素标签名:div,拥有一个值为mydiv的ID. <div id="myDiv"></div> //通过getElementById()方法取得元素及其标签名 var div = document.getElementById("myDiv"); alert(div.tagName); //"DIV",HTML中标签名始终以全部大写表示,xml中,标签名则始终会与源代码中保持一致. alert(div.tagName == div.nodeName); //true
3.1 HTML元素
id,元素在文档中的唯一标识符。 title,有关元素的附加说明信息,一般通过工具提示条显示出来。 lang,元素内容的语言代码,很少使用。 dir,语言的方向,值为"ltr"(left-to-right,从左至右)或"rtl"(right-to-left,从右至左),也很少使用。 className,与元素的 class 特性对应,即为元素指定的 CSS 类。 eg1: <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> //元素中指定的所有信息,都可以通过下列 JavaScript 代码取得 var div = document.getElementById("myDiv"); alert(div.id); //"myDiv"" alert(div.className); //"bd" alert(div.title); //"Body text" alert(div.lang); //"en" alert(div.dir); //"ltr" //当然,像下面这样通过为每个属性赋予新的值,也可以修改对应的每个特性: div.id = "someOtherId"; div.className = "ft"; div.title = "Some other text"; div.lang = "fr"; div.dir ="rtl";
3.2 操作特性
取得特性,操作特性的DOM方法主要有3个:getAttribute()、 setAttribute()和 removeAttribute() .
//getAttribute()方法,取得特性,给定名称的特性不存在,getAttribute()返回null.自定义特性不存在,显示undefined. var div = document.getElementById("myDiv"); alert(div.getAttribute("id")); //"myDiv" alert(div.getAttribute("class")); //"bd" alert(div.getAttribute("title")); //"Body text" alert(div.getAttribute("lang")); //"en" alert(div.getAttribute("dir")); //"ltr"
//setAttribute()设置特性,接收2个参数:要设置的特性名和值. //特性存在,指定值替换现有的值;特性不存在,创建该属性并设置相应的值. div.setAttribute("id", "someOtherId"); div.setAttribute("class", "ft"); div.setAttribute("title", "Some other text"); div.setAttribute("lang","fr"); div.setAttribute("dir", "rtl"); //所有特性都是属性,直接给属性赋值可以设置特性的值. div.id = "someOtherId"; div.align = "left";
//removeAttribute(),用于彻底删除元素的特性. div.removeAttribute("class");
3.3 attributes属性
Element类型是使用attributes属性的唯一一个DOM节点类型.attributes属性中包含一个NameNodeMap,与NodeList类似,也是一个动态集合.元素的每一个特性都由一个Attr节点表示,每个节点都保存在NameNodeMap对象中.
NameNodeMap对象拥有的方法:
- getNamedItem(name):返回 nodeName 属性等于 name 的节点;
- removeNamedItem(name):从列表中移除 nodeName 属性等于 name 的节点;
- setNamedItem(node):向列表中添加节点,以节点的 nodeName 属性为索引;
- item(pos):返回位于数字 pos 位置处的节点。
4 Text类型
文本节点由 Text 类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML 字符,但不能包含 HTML 代码。
Text 节点具有以下特征:
- nodeType 的值为 3;
- nodeName 的值为"#text";
- nodeValue 的值为节点所包含的文本;
- parentNode 是一个 Element;
- 不支持(没有)子节点。可以通过 nodeValue 属性或 data 属性访问 Text 节点中包含的文本,这两个属性中包含的值相同。对 nodeValue 的修改也会通过 data 反映出来,反之亦然。使用下列方法可以操作节点中的文本。
- appendData(text):将 text 添加到节点的末尾。
- deleteData(offset, count):从 offset 指定的位置开始删除 count 个字符。
- insertData(offset, text):在 offset 指定的位置插入 text。
- replaceData(offset, count, text):用 text 替换从 offset 指定的位置开始到 offset+count 为止处的文本。
- splitText(offset):从 offset 指定的位置将当前文本节点分成两个文本节点。
- substringData(offset, count):提取从 offset 指定的位置开始到 offset+count 为止处的字符串。
<!-- 没有内容,也就没有文本节点 --> <div></div> <!-- 有空格,因而有一个文本节点 --> <div> </div> <!-- 有内容,因而有一个文本节点 --> <div>Hello World!</div>
5 Comment类型
注释在 DOM 中是通过 Comment 类型来表示的,可以通过 nodeValue 或 data 属性来取得注释的内容。
Comment 节点具有下列特征:
- nodeType 的值为 8;
- nodeName 的值为"#comment";
- nodeValue 的值是注释的内容;
- parentNode 可能是 Document 或 Element;
- 不支持(没有)子节点。
//注释节点可以通过其父节点来访问,以下面的代码为例。 <div id="myDiv"><!--A comment --></div> //注释节点是<div>元素的一个子节点,因此可以通过下面的代码来访问它。 var div = document.getElementById("myDiv"); var comment = div.firstChild; alert(comment.data); //"A comment"
var comment = document.createComment("A comment "); //传递注释文本并创建注释节点
6 CDATASection类型
CDATASection 类型只针对基于 XML 的文档,表示的是 CDATA 区域。与 Comment 类似,CDATASection 类型继承自 Text 类型,因此拥有除 splitText()之外的所有字符串操作方法。
CDATASection 节点具有下列特征:
- nodeType 的值为 4;
- nodeName 的值为"#cdata-section";
- nodeValue 的值是 CDATA 区域中的内容;
- parentNode 可能是 Document 或 Element;
- 不支持(没有)子节点。
7 DocumentType类型
DocumentType-Type 包含着与文档的 doctype 有关的所有信息,它具有下列特征:
- nodeType 的值为 10;
- nodeName 的值为 doctype 的名称;
- nodeValue 的值为 null;
- parentNode 是 Document;
- 不支持(没有)子节点。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> //DocumentType 的 name 属性中保存的就是"HTML": alert(document.doctype.name); //"HTML"
8 DocumentFrament类型
在所有节点类型中,只有 DocumentFragment 在文档中没有对应的标记。 DOM 规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
DocumentFragment 节点具有下列特征:
- nodeType 的值为 11;
- nodeName 的值为"#document-fragment";
- nodeValue 的值为 null;
- parentNode 的值为 null;
- 子节点可以是 Element、 ProcessingInstruction、 Comment、 Text、 CDATASection 或EntityReference。
9 Attr类型
元素的特性在 DOM 中以 Attr 类型来表示。在所有浏览器中(包括 IE8),都可以访问 Attr 类型的构造函数和原型。从技术角度讲,特性就是存在于元素的 attributes 属性中的节点。
特性节点具有下列特征:
- nodeType 的值为 2;
- nodeName 的值是特性的名称;
- nodeValue 的值是特性的值;
- parentNode 的值为 null;
- 在 HTML 中不支持(没有)子节点;
- 在 XML 中子节点可以是 Text 或 EntityReference。
1.2 DOM操作技术
总结:DOM是API,用于访问和操作HTML和XML文档。DOM1把HTML和XML文档看作一个层次化的节点树,可以操作这个节点树。
DOM由各种节点构成:
最基本的节点类型是Node类型,用于抽象地表示文档中一个独立的部分。其他所有类型都继承自Node。
Document类型:整个文档,是一组分层节点的根节点。document对象,可以查询和取得节点。
Element类型:Element节点表示文档中所有的HTML或XML元素,可以用来操作这些元素的内容和特性。
节点类型:文本内容类型、注释类型、文档类型、 CDATA区域类型、文档类型。
2 DOM扩展
2.1 选择符API
2.2 元素遍历
2.3 HTML5
2.4 专有扩展
3 DOM2和DOM3
3.1 DOM变化
3.2 样式
3.3 遍历
3.4 范围
4 事件
HTML与Javascript之间的交互是通过事件实现的。
事件:文档或浏览器窗口中发生的一些特定的交互瞬间。事件就是用户或浏览器自身执行的某种动作。比如:click、load、mouseover都是事件名字。事件可以通过侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。
事件流:描述从页面中接收事件的顺序。IE的事件流叫时间冒泡流(event bubbling),Netscape Communicator的事件流是时间捕获流。
4.1 事件流
事件冒泡:事件开始时由最具体的元素接收,然后逐级传播到较为不具体的节点(文档)。
比如:
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html>
点击页面中的div元素,这个click事件会按照如下顺序传播。
事件流如图:
4.2 事件处理程序
事件处理程序[事件侦听器]:响应某个事件的函数。事件处理程序的名字都是以"on"开头。比如click事件的事件处理程序是onclick,load事件的事件处理程序是onload.