JavaScript高级-unit10 DOM
简介
DOM描述了一个层次化的节点树,允许开发人员添加、移除和修改页面。
节点
根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:
- 整个文档是一个文档节点
- 每个 HTML 元素是元素节点
- HTML 元素内的文本是文本节点
- 每个 HTML 属性是属性节点
- 注释是注释节点
每个节点都有一个nodeType属性,用于表明节点的类型。并不是所有节点类型都受到Web浏览器支持,最常用的事元素和文本节点。
Node.ELEMENT_NODE(1); // 元素
Node.ATTRIBUTE_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);
因为IE没有公开Node类型的构造函数,所以确定节点类型如下:
// nodeType 是只读的 if (someNode.nodeType == 1) { alert("Node is an element"); }
Node类型
1、nodeName 和 nodeValue 属性
// 使用前先检查节点类型,确认是否是一个元素,对于元素节点 // nodeName 保存的是标签名(节点的名称),nodeValue = null (节点的值); if (someNode.nodeType == 1) { value = someNode.nodeName; // nodeName的值是元素的标签名 }
2、 节点关系
每个节点都有一个 childNodes 属性,保存着一个 NodeList 对象。
- 是一个数组对象,保存一组有序的节点,可以通过位置访问节点。有length属性,但不是数组实例。
- 基于DOM结构动态执行查询结果,结构的变化能够自动反应着NodeList对象中。
- 可以用 item() 方法访问其中的节点。
var firstChild = someNode.childNodes[0]; var secondChild = someNode.childNodes.item(1); // 将NodeList对象转化为数组 function convertToArray(nodes) { var array = null; try { array = Array.prototype.slice.call(node,0); // 针对非 IE 浏览器 } catch { array = new Array(); for (var i=0, len=nodes.length; I < len; i++) { array.push(nodes[I]); } } return array; }
- parentNode 属性:指向文档树中的父节点
- 父节点的 firstChild 和 lastChild 属性指向第一个和最后一个
- previousSibling: 上一个同胞节点
- nextSibling: 下一个同胞节点
- hasChildNodes(): 节点包含一个或多个子节点返回 true
- ownerDocument: 指向表示整个文档的文档节点
- document.documentElement - 全部文档
- document.body - 文档的主体
3、操作节点
以下方法都需要取得父节点(使用 parentNode 属性)
- appendChild(): 向 childNodes 列表末尾添加一个节点。如果已存在,从原来位置移动到新位置
- insertBefore(): 插入节点,接受两个参数,要出入的节点和参考节点(谁的前面)
- replaceChild(): 替换节点,接受两个参数,要插入的节点和要替换的节点
- removeChild(): 移除节点
以下两个方法是所有类型的节点都有
- cloneNode(): 创建节点的副本,参数为true,复制节点及整个子节点树,false只复制节点本身,没有父节点,需要用上面方法添加到文档中
- normalize(): 处理文档树中的文本节点。可能会出现文本节点不包含文本,或者接连出现两个文本节点。调用这方法,如果找到空文本,则删除;找到相邻文本节点,则合并为一个
Document 类型
JavaScript通过 Document 类型表示文档。在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个HTML页面。而且 document 也是 window 对象的一个属性,因此可以将其作为全局对象来访问。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。
- nodeName 的值为 “#document”;
- nodeValue 的值为 null;
- parentNode 的值为 null;
- ownerDocument 的值为 null;
- 其子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction 或 Comment。
1、文档的子节点
内置的访问子节点的快捷方式:
- documentElement 属性:始终指向HTML页面的<html>元素
- childNodes 列表访问文档元素
作为内置的HTMLDocument对象,document 对象有一个 body 属性,直接指向 <body> 元素。
// 所有浏览器都支持 var html = document.documentElement; // 取得对<html>对引用 var body = document.body; // 取得对<body>对引用
用不着在 document 对象上调用 appendChild()、removeChild()和replaceChild()方法,因为文档类型(如果存在)是只读的,而且它只能有一个元素子节点(通常早存在)。
2、文档信息
document 对象还有一些标准的Document对象所没有的属性。这些对象表现的网页的一些信息。
// 取的文档标题 var originalTitle = document.title; // 设置文档标题 document.title = "New page title"; // 取得完成的URL var url = document.URL; // 取的域名 var domain = document.domain; // 取的来源页面的URL var referrer = document.referrer;
- 只有 domain 可以设置,如果包含子域名,不能设置为URL中不包含的域。
- 由于跨域安全限制,来自不同子域的页面无法通过 Javascript 通信,可以将每个页面的document.domain设置为相同值,就可以互相访问对方包含的JavaScript对象了。
- 如果域名一开始是“松散的”,就不能设为“紧绷的”,设为“wrox.com”,就不能设为“p2p.wrox.com”。
3、查找元素
取得特定的某个或某组元素的引用,然后执行一些操作。
- getElementById(): 要获取的元素 id,返回第一个出现的,如果没找到返回 null
- getElementsByTagName(): 参数(要取得元素的标签名),返回包含零个或多个元素的 NodeList。在HTML文档中,返回一个 HTMLCollection 对象,作为动态集合。传入“*”,取得文档中的所有元素
- getElementsByName(): 返回给定 name 特性的所有元素。最常用取得单选按钮(单选按钮必须具有相同的 name 特性)
// 取得页面中所有的<img>元素,并返回一个HTMLCollection var images = document.getElementsByTagName("img"); alert(images.length); // 图像的数量 alert(images[0].src); // 第一个图像元素的src特性 alert(images.item(0).src); // 第一个图像元素的src特性
HMTLCollection 对象
- 有一个 namedItem() 方法,可以通过元素的 name 特性取得集合中的项
- 支持按名称访问项
<img src="my image.gif" name="myImage"> var myImage = images.namedItem("myImage"); var myImage = images["myImage"];
4、特殊集合
除了属性和方法,document 对象还有一些特殊的集合,都是 HTMLCollection对象,
- document.anchors ,包含文档中所有带 name 特性的 <a> 元素;
- document.applets ,所有的 <applet>元素,不建议使用;
- document.forms ,文档中所有的 <form> 元素;
- document.images ,文档中所有的 <img> 元素;
- document.links ,文档中所有带 href 特性的 <a> 元素。
5、DOM一致性检测
document.implementation 属性提供相应信息和功能的对象,与浏览器对 DOM 的实现直接对应。
规定一个方法:hasFeature(),接受两个参数:要检测的 DOM 功能名称及版本号
var hasXmlDom = document.implementation.hasFeature("XML","1.0");
6、文档写入
- write(): 原样写入
- writeln(): 在字符串的末尾添加一个换行符(n)
- open(): 打开网页的输出流
- close(): 关闭网页的输出流
// 用 write() 和 written() 动态地包含外部资源 <html> <head> <title>document.write() Example</title> </head> <body> <script type="text/javascript"> document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>"); </script> </body>
严格型 XHTML 文档不支持文档写入。
Element 类型
Element 类型用于表现 XML 或 HTML 元素,提供了对元素标签名、字节点及特性的访问。
- nodeType 的值为 1;
- nodeName 的值为元素的标签名;
- nodeValue 的值为 null;
- parentNode 可能是 Document 或 Element;
- 其子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或 EntityReference;
- 访问元素标签名,可以是使用 nodeName 属性或者 tagName 属性。
<div id="myDiv"></div> var div = document.getElementById("myDiv"); alert(div.tagName); // "DIV" alert(div.tagName == div.nodeName); // true
在 HTML 中,标签名始终都以全部大写表示;而在 XML(有时包括 XHTML),标签名始终与源代码中的保持一致。
if (element.tagName.toLowerCase() == "div") { // 适用于任何文档 // ... }
1、HTML 元素
所有的 HTML 元素都由 HTMLElement 类型表示。每个 HTML 元素都存在的下列标准特性。
- id,元素中文档中的唯一标识符
- title,有关元素的附加说明信息,一般通过工具提示条现实出来
- lang,元素内容的语言代码
- dir,语言的方向,值为“ltr”(左到右),或“rtl”(右到左)
- className,与元素的 class 特性对应,即为元素指定的CSS类。(是 ECMAScript 的保留字)
// 上述这些属性都可以用来取得或修改相应的特性值 <div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> var div = document.getElementById("myDiv"); alert(div.id); div.id = "someOtherId"; alert(div.className); div.className = "ft";
2、取得特性
每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。
- getAttribute(): 取得特性,传递的特性名与实际的特性名相同,可以是自定义的特性。
- setAttribute(): 设置特性。设置的特性名统一转为小写。
- removeAttribute(): 不仅会清除特性值,也会从元素中完成删除特性。
有两类特殊的特性:
- style: 通过getAttribute()访问返回特性值中包含的文本,通过属性来访问返回一个对象。
- onclick: 通过 getAttribute()返回相应代码的字符串,访问属性时返回函数。
div.setAttribute("id","someOtherId");
3、attributes 属性
attributes 属性包含一系列节点,每个节点的 nodeName 就是特性的名称,而节点的 nodeValue 就是特性的值。
包含一个 NamedNodeMap,“动态”集合。
- getNamedItem():返回 nodeName 等于 name 的节点
- removeNamedItem():从列表中移除 nodeName 属性等于 name 的节点
- setNamedItem():向类表添加节点,以节点的 nodeName 属性为索引
- item(pos):返回位于数字pos 的节点
var id = element.attributes.getNamedItem("id").nodeValue; var id = element.attributes["id"].nodeValue;
遍历元素的特性,attributes 属性可以派上用场。
function outputAttributes(element) { var pairs = new Array(), attrName, attrValue, y, len; for(y = 0, len = element.attributes.length; y < len; y++) { attrName = element.attributes[y].nodeName; attrValue = element.attributes[y].nodeValue; if (element.attributes[y].specified) { pairs.push(attrName + "=\"" + attrValue + "\""); } } return pairs.join(" "); } // attributes 对象的中的特性,不同浏览器返回的顺序不同 // IE7 及更早版本会返回 HTML 元素中所有可能的特性,包括没有指定的特性。每个特性节点都有一个 specified 的属性,true:在 HTML 中指定来相应特性,要么可以通过 setAttribute() 方法设置了该特性。
4、创建元素
// 接受一个参数,即要创建元素的标签名 var div = document.createElement("div"); // 也为新元素设置了 ownerDocument 属性,还可以操作元素的特性,添加更多的子节点 div.id = "myNewDiv"; div.className = "box"; // 新元素添加到文档树中 appendChild()、insertBefore()、replaceChild() document.body.appendChild(div);
在 IE 中可以另一种方法,传入完整的元素标签
var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div>");
5、元素的子节点
不同浏览器看待节点是不同的。
如果要通过 childNodes 属性遍历子节点,不要忘了浏览器之间的区别,通常先检查一下 nodeType 属性
for (var y = 0, len = element.childNodes.length; y < len; y++) { // 表示是元素节点 if (element.childNodes[y].nodeType == 1) { // ... } }
通过特定的标签名取得子节点或后代节点
var ul = document.getElementById("myList"); var items = ul.getElementsByTagName("li");
Text 类型
文本节点由 Text 类型表示,包含纯文本内容,即包含转义后的 HTML 字符,但不包含 HTML 代码。
- 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 为止的文本。
- splitText(offset):从 offset 指定的位置将当前文本节点分为两个文本节点。
- substringData(offset, count):提取从 offset 指定的位置开始到 offset + count 为止的字符串。
在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。
// 没有内容,也就没有文本节点 <div></div> // 有空格,因而有一个文本节点 <div> </div> // 有内容,因而有一个文本节点 <div>hello world!</div> // 访问,先取得引用 var textNode = div.firstChild; // 获取 div.childNodes[0] textNode.nodeValue = "some other message";
在修改文本节点时,此时的字符串会经 HTML 编码(转义)。
1、创建文本节点
- document.createTextNode():创建文本节点,接受一个参数,要插入的文本,也会进行转义。
var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee"); element.appendChild(anotherTextNode); document.body.appendChild(element);
如果两个文本节点是相邻的同胞节点,那么这两个节点中的文本会连起来显示,中间不会有空格。
2、规范化文本节点
- normalize():在一个包含两个或多个文本节点的父元素上调用,则会将所有文本节点合并成一个节点。
Comment 类型
- nodeType 的值为 8;
- nodeName 的值为 “#comment”;
- nodeValue 的值是注释的内容;
- parentNode 可能是 Document 或 Element;
- 不支持(没有)子节点。
和 Text 类型相似,可以通过 nodeValue 或 data 属性来取得注释的内容。
<div id="myDiv"><!-- A comment --></div> // 通过父节点访问 var div = document.getElementById("myDiv"); var comment = div.firstChild; alert(comment.data);
使用 document.createComment() 为其传递注释文本也可以创建注释节点。
var comment = document.createComment("A comment");
如果要访问注释节点,一定要保证他们是 <html> 元素的后代。
DocumentFragment 类型(文档片段)
- 在文档中没有对应的标记;
- nodeType 的值为 11;
- nodeName 的值为 “#document-fragment”;
- nodeValue 的值为 null;
- parentNode 的值为 null;
不能把文档片段直接添加到文档中,但可以作为一个“仓库”,保存将来可能添加到文档中的节点。如果将文档中的节点添加到文档片段中,就会从文档树移除该节点,文档片段本身不会成为文档树的一部分。
// 创建文档片段 var fragment = document.createDocumentFragment(); // 为 ul 元素添加3个列表项 var ul = document.getElementById("myList"); var li = null; for (var i=0; i < 3; i++) { li = document.createElement("li"); li.appendChild(document.createTextNode("Item " + (i+1))); fragment.appendChild(li); } ul.appendChild(fragment);
Attr 类型
元素的特性在 DOM 中以 Attr 类型来表示。特性就是存在于元素的 attributes 属性中的节点。
- nodeType 的值 2;
- nodeName 的值是特性的名称;
- nodeValue 的值是特性的值;
- parentNode 的值为 null;
- 在 HTML 中不支持子节点;
- 在 XML 中子节点可以是 Text 或 EntityReference。
尽管是节点,但不是 DOM 文档树但一部分。有3个属性:
- name:特性名称(与 nodeName 相同);
- value:特性的值(与 nodeValue 的值相同);
- specified:一个布尔值,区别特性在代码中是指定的,还是默认的。
// 创建 var attr = document.createAttribute("align"); attr.value = "left"; // 添加到元素中 element.setAttributeNode(attr);