Javascript学习8 - 脚本化文档(Document对象)
每个Web浏览器窗口(或帧)显示一个HTML文档,表示这个窗口的Window对象有一个document属性,它引用了一个Document对象。
一个文档对象模型或者说DOM就是一个API,它定义了如何访问组成一个文档的对象。W3C定义了一个标准的DOM,使用Document API,可以作用于HTML和XML文档的通用功能,添加特定于HTML的属性和方法。
Document对象的详细属性可以见:http://www.w3school.com.cn/htmldom/dom_obj_document.asp
8.1 Document属性
cookie : 允许Javascript程序读写HTTP cookie.
lastModfied : 一个字符串,包含文档修改日期
location : 等价于URL,已经废弃。
referrer : 文档URL,包含把浏览器带到当前文档的链接。
title : 文档标记<title>之间的文本。
URL : 一个字符串,声明装载文档的URL。
8.2 文档对象集合 【遗留DOM】
anchors[] : 文档中所有的锚
applets[] : 代表文档中的Java applet
forms[] : 代表文档中的form元素。
images[] : 所有<img>元素的集合
links[] : 所有超链接Link对象
以上这些称之为遗留DOM集合,它们可以脚本化,但是,它们不能改变文档的结构。可以查看并修改链接的目标地址,从表单元素读取和写入值,但是,无法改变文档相互的结构。
8.3 命名document对象
一般来说,可以使用基于数字来索引文档对象集合元素,但是,基于位置索引不稳定,小的文档改变顺序可以破坏代码。
所以,可以为文档元素分配名字,通过名字来引用这些元素。
例如,以下form元素的访问:
<form name="f1">....</form>
可以这样访问:
document.forms[0]
也可以这样:
document.forms.f1 或者 document.forms["f1"]
有以下表单
<form name="shipping"> <input type="text" name="zipcode"> </form>
可以做以访问:
在HTML中,把函数赋予一个事件句柄属性来定义它们,以下的HTML代码加下事件句柄:
<form name="myform" onsubmit="return validateform();">...</form>
除了在onsubmit中加入javascript函数外,也可以在JS中直接把函数赋予事件句柄属性
8.5 W3C DOM 节点
W3C DOM 取代了遗留DOM,强大而标准化,在介绍DOM之前,有一些预备知识:
①需要把文档表示为树
HTML文档在DOM中表示为对象的一棵树,树中有各种各样的节点,节点之上的节点是父节点(parent),直接位于一个节点下层的节点是子节点(children).位于同一层次的节点是兄弟节点(sibling).一个节点的下一个层次节点的集合是那个节点的扣代(descentdant).一个节点的父节点、祖父节点及其它所有位于其它的节点集合是那个节点的祖先(ancestor)
②节点Node
一个节点表示为一个Node接口,Node接口定义了遍历和操作树的属性和方法。
Node.childNodes 返回节点的孩子集合
Node.firstChild,lastChild,nextSibling,previousSibling,parentNode 属性提供了遍历节点的树的一种方法
Node.appendChild(), removeChild(),replaceChild(),insertBefore() 这些方法能够向一个文档树中添加节点或者移动节点。
③节点的类型
每个Node对象都有一个nodeType属性,它指定了节点是什么类型。
常见节点类型:
Element Node.ELEMENT_NODE 1
Text Node.TEXT_NODE 3
Document Node.DOCUMENT_NODE 9
Comment Node.COMMENT_NODE 8
DocumentFragment Node.DOCUMENT_FRAGMENT_NODE 11
Attr Node.ATTRIBUTE_NODE 2
DOM树部的Node是一个Document对象,这个对象的documentElemtn属性引用一个Element对象,它代表了文档的根元素。对于HTML文档,这是<html>标记。
在一个DOM树中只有一个Document对象,DOM树的大部分节点是表示标记的Element对象和表示文本串的Text对象。
DOM API的部门类分层图见15-2
④属性
用Element接口的getAttribute()方法、SetAttribute()方法和RemoveAttribute()方法可以获得、增加、删除一个元素的属性(如img的src属性和width属性)
8.6 DOM HTML API
HTMLDocument是HTML专有的Document接口的子接子。HTMLElement是HTML专有的Element接口的子接口。另外,DOM为许多HTML元素定义了标记专有的接口,如HTMLBodyElement、HTMLTitleElement、HTMLInputElement...这些标记专有的接口通常定义了HTML标记属性的属性集合。
● HTMLDocument接口定义了支持各种文档的属性和方法,包括location、forms[]数组、write()方法
● HTMLElement接口定义了id,style,title,lang,dir和className属性。
● 其它的专有的HTML..相关接口也定义了专有的属性和方法,如HTMLInputElement定义了focus(),blur()...
一个访问DOM接口的例子:
var t = new Text("this is a new text node"); // No such constructor
取而代之的是,DOM在Document接口中定义了大量有用的工厂方法(factory method).因此,要为文档创建一个Text节点,可以使用以下代码:
var t = document.createTextNode("this is a new text node");
8.7 遍历文档
DOM把一个HTML文档表示为Node对象的树,对于任何一个树形结构来说,最常用的事就是遍历树。
→依次检查树的每个节点。如以下代码所示:
除了childNodes属性,Node接口还定义了其它几个有用的属性,包括firstChild,lastChild,nextSibling,previousSibling.
→获得DOM节点下所有的文本:
8.8 在文档中查找元素
● getElementsByTagName()方法
调用getElementsByTagName(param)方法,返回一个数组,该数组元素是文档中所有param元素。
例如:
var tables = document.getElementsByTagName("table");
alert("This document contains " + tables.length + " tables"); // 文档中定义了多少个Table
如果把特殊字符"*"传递给getElementsByTagName,它将返回文档中所有元素的列表。
有时不想操作元素列表,而想操作文档中一个特定元素,如果对文档中结构有充分的了解,仍可以使用getElementsByTagName方法:
var myParagraph = document.getElementsByTagName("p")[3];
但这并不是最好的方法。最好的办法是使用getElementsById()方法、
● getElementById()方法
getElementById方法不像getElementsByTagName返回一个数组列表,因为每个id属性值唯一,所以getElementById方法只返回一个元素。
getElementById是一个很重要的方法,非常常见。
以下通过getElementById方法获得表格中某个Cell的值。
与getElementById方法相似,它查询元素的name属性,而不是id属性。另外,一个文档中的name属性可能不唯一,所以getElementsByName方法返回的是元素的数组,而不是一个元素。
8.9 修改文档内容
DOM API的真正威力在于用Javascript动态修改文档的特性。
→对元素的一个列表按字母排序
→把文档内容转换为大小写
→把一个节点的父节点重定为<b>元素
8.10 修改节点属性
可以调用element.setAttribute()方法来设置一个节点的属性,如:
8.11 Document段
DocumentFragment是一种特殊类型的节点,它自身不出现在文档中,只作为连续节点集合的临时容器,并允许将这些节点作为一个对象来操作。
把一个DocumentFragment插入文档时,插入的不是DocumentFragment本身,而是它所持有的子节点。
8.12 给文档添加内容
Document.createElement()方法和Document.createTextNode()方法创建新的Element节点和Text节点。
而方法Node.appendChild()、Node.insertBefore()和Node.replaceChild()可以用来将它们添加到一个文档。
8.13 innerHTML属性
该属性可以用来获得和设置结点中HTML文本。例如:
一个文档对象模型或者说DOM就是一个API,它定义了如何访问组成一个文档的对象。W3C定义了一个标准的DOM,使用Document API,可以作用于HTML和XML文档的通用功能,添加特定于HTML的属性和方法。
Document对象的详细属性可以见:http://www.w3school.com.cn/htmldom/dom_obj_document.asp
8.1 Document属性
cookie : 允许Javascript程序读写HTTP cookie.
lastModfied : 一个字符串,包含文档修改日期
location : 等价于URL,已经废弃。
referrer : 文档URL,包含把浏览器带到当前文档的链接。
title : 文档标记<title>之间的文本。
URL : 一个字符串,声明装载文档的URL。
8.2 文档对象集合 【遗留DOM】
anchors[] : 文档中所有的锚
applets[] : 代表文档中的Java applet
forms[] : 代表文档中的form元素。
images[] : 所有<img>元素的集合
links[] : 所有超链接Link对象
以上这些称之为遗留DOM集合,它们可以脚本化,但是,它们不能改变文档的结构。可以查看并修改链接的目标地址,从表单元素读取和写入值,但是,无法改变文档相互的结构。
8.3 命名document对象
一般来说,可以使用基于数字来索引文档对象集合元素,但是,基于位置索引不稳定,小的文档改变顺序可以破坏代码。
所以,可以为文档元素分配名字,通过名字来引用这些元素。
例如,以下form元素的访问:
<form name="f1">....</form>
可以这样访问:
document.forms[0]
也可以这样:
document.forms.f1 或者 document.forms["f1"]
有以下表单
<form name="shipping"> <input type="text" name="zipcode"> </form>
可以做以访问:
1document.forms[0].elements[0]
2document.shipping.zipcode
8.4 Document对象上的事件句柄2document.shipping.zipcode
在HTML中,把函数赋予一个事件句柄属性来定义它们,以下的HTML代码加下事件句柄:
<form name="myform" onsubmit="return validateform();">...</form>
除了在onsubmit中加入javascript函数外,也可以在JS中直接把函数赋予事件句柄属性
1document.myform.onsubmit = validateform; //注意,不是调用函数,不需要()
8.5 W3C DOM 节点
W3C DOM 取代了遗留DOM,强大而标准化,在介绍DOM之前,有一些预备知识:
①需要把文档表示为树
HTML文档在DOM中表示为对象的一棵树,树中有各种各样的节点,节点之上的节点是父节点(parent),直接位于一个节点下层的节点是子节点(children).位于同一层次的节点是兄弟节点(sibling).一个节点的下一个层次节点的集合是那个节点的扣代(descentdant).一个节点的父节点、祖父节点及其它所有位于其它的节点集合是那个节点的祖先(ancestor)
②节点Node
一个节点表示为一个Node接口,Node接口定义了遍历和操作树的属性和方法。
Node.childNodes 返回节点的孩子集合
Node.firstChild,lastChild,nextSibling,previousSibling,parentNode 属性提供了遍历节点的树的一种方法
Node.appendChild(), removeChild(),replaceChild(),insertBefore() 这些方法能够向一个文档树中添加节点或者移动节点。
③节点的类型
每个Node对象都有一个nodeType属性,它指定了节点是什么类型。
常见节点类型:
Element Node.ELEMENT_NODE 1
Text Node.TEXT_NODE 3
Document Node.DOCUMENT_NODE 9
Comment Node.COMMENT_NODE 8
DocumentFragment Node.DOCUMENT_FRAGMENT_NODE 11
Attr Node.ATTRIBUTE_NODE 2
DOM树部的Node是一个Document对象,这个对象的documentElemtn属性引用一个Element对象,它代表了文档的根元素。对于HTML文档,这是<html>标记。
在一个DOM树中只有一个Document对象,DOM树的大部分节点是表示标记的Element对象和表示文本串的Text对象。
DOM API的部门类分层图见15-2
④属性
用Element接口的getAttribute()方法、SetAttribute()方法和RemoveAttribute()方法可以获得、增加、删除一个元素的属性(如img的src属性和width属性)
8.6 DOM HTML API
HTMLDocument是HTML专有的Document接口的子接子。HTMLElement是HTML专有的Element接口的子接口。另外,DOM为许多HTML元素定义了标记专有的接口,如HTMLBodyElement、HTMLTitleElement、HTMLInputElement...这些标记专有的接口通常定义了HTML标记属性的属性集合。
● HTMLDocument接口定义了支持各种文档的属性和方法,包括location、forms[]数组、write()方法
● HTMLElement接口定义了id,style,title,lang,dir和className属性。
● 其它的专有的HTML..相关接口也定义了专有的属性和方法,如HTMLInputElement定义了focus(),blur()...
一个访问DOM接口的例子:
1var n = document.documentElement; // This is a Node object.
2var children = n.childNodes; // This is a NodeList object.
3var head = children.item(0); // Here is one way to use a NodeList.
4var body = children[1]; // But this way is easier!
另一个需要了解的是:DOM标准定义了接口,而不是类,所以它没有定义任何构造函数方法。如果想创建一个Text对象,把它插入文档,不能用如下代码:2var children = n.childNodes; // This is a NodeList object.
3var head = children.item(0); // Here is one way to use a NodeList.
4var body = children[1]; // But this way is easier!
var t = new Text("this is a new text node"); // No such constructor
取而代之的是,DOM在Document接口中定义了大量有用的工厂方法(factory method).因此,要为文档创建一个Text节点,可以使用以下代码:
var t = document.createTextNode("this is a new text node");
8.7 遍历文档
DOM把一个HTML文档表示为Node对象的树,对于任何一个树形结构来说,最常用的事就是遍历树。
→依次检查树的每个节点。如以下代码所示:
1function countTags(n) { // n is a Node
2 var numtags = 0; // Initialize the tag counter
3 if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) // Check if n is an Element
4 numtags++; // Increment the counter if so
5 var children = n.childNodes; // Now get all children of n
6 for(var i=0; i < children.length; i++) { // Loop through the children
7 numtags += countTags(children[i]); // Recurse on each one
8 }
9 return numtags; // Return the total
10}
2 var numtags = 0; // Initialize the tag counter
3 if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) // Check if n is an Element
4 numtags++; // Increment the counter if so
5 var children = n.childNodes; // Now get all children of n
6 for(var i=0; i < children.length; i++) { // Loop through the children
7 numtags += countTags(children[i]); // Recurse on each one
8 }
9 return numtags; // Return the total
10}
除了childNodes属性,Node接口还定义了其它几个有用的属性,包括firstChild,lastChild,nextSibling,previousSibling.
→获得DOM节点下所有的文本:
1 function getText(n) {
2 // Repeated string concatenation can be inefficient, so we collect
3 // the value of all text nodes into an array, and then concatenate
4 // the elements of that array all at once.
5 var strings = [];
6 getStrings(n, strings);
7 return strings.join("");
8
9 // This recursive function finds all text nodes and appends
10 // their text to an array.
11 function getStrings(n, strings) {
12 if (n.nodeType == 3 /* Node.TEXT_NODE */)
13 strings.push(n.data);
14 else if (n.nodeType == 1 /* Node.ELEMENT_NODE */) {
15 // Note iteration with firstChild/nextSibling
16 for(var m = n.firstChild; m != null; m = m.nextSibling) {
17 getStrings(m, strings);
18 }
19 }
20 }
21 }
2 // Repeated string concatenation can be inefficient, so we collect
3 // the value of all text nodes into an array, and then concatenate
4 // the elements of that array all at once.
5 var strings = [];
6 getStrings(n, strings);
7 return strings.join("");
8
9 // This recursive function finds all text nodes and appends
10 // their text to an array.
11 function getStrings(n, strings) {
12 if (n.nodeType == 3 /* Node.TEXT_NODE */)
13 strings.push(n.data);
14 else if (n.nodeType == 1 /* Node.ELEMENT_NODE */) {
15 // Note iteration with firstChild/nextSibling
16 for(var m = n.firstChild; m != null; m = m.nextSibling) {
17 getStrings(m, strings);
18 }
19 }
20 }
21 }
8.8 在文档中查找元素
● getElementsByTagName()方法
调用getElementsByTagName(param)方法,返回一个数组,该数组元素是文档中所有param元素。
例如:
var tables = document.getElementsByTagName("table");
alert("This document contains " + tables.length + " tables"); // 文档中定义了多少个Table
如果把特殊字符"*"传递给getElementsByTagName,它将返回文档中所有元素的列表。
有时不想操作元素列表,而想操作文档中一个特定元素,如果对文档中结构有充分的了解,仍可以使用getElementsByTagName方法:
var myParagraph = document.getElementsByTagName("p")[3];
但这并不是最好的方法。最好的办法是使用getElementsById()方法、
● getElementById()方法
getElementById方法不像getElementsByTagName返回一个数组列表,因为每个id属性值唯一,所以getElementById方法只返回一个元素。
getElementById是一个很重要的方法,非常常见。
以下通过getElementById方法获得表格中某个Cell的值。
1var tableOfContents = document.getElementById("TOC");
2var rows = tableOfContents.getElementsByTagName("tr");
3var numrows = rows.length;
● getElementsByName()方法2var rows = tableOfContents.getElementsByTagName("tr");
3var numrows = rows.length;
与getElementById方法相似,它查询元素的name属性,而不是id属性。另外,一个文档中的name属性可能不唯一,所以getElementsByName方法返回的是元素的数组,而不是一个元素。
1var link = document.getElementsByName("top")[0];
2// Find all <input type="radio" name="shippingMethod"> elements
3var choices = document.getElementsByName("shippingMethod");
2// Find all <input type="radio" name="shippingMethod"> elements
3var choices = document.getElementsByName("shippingMethod");
8.9 修改文档内容
DOM API的真正威力在于用Javascript动态修改文档的特性。
→对元素的一个列表按字母排序
1<script>
2function sortkids(e) {
3 // This is the element whose children we are going to sort
4 if (typeof e == "string") e = document.getElementById(e);
5
6 // Transfer the element (but not text node) children of e to a real array
7 var kids = [];
8 for(var x = e.firstChild; x != null; x = x.nextSibling)
9 if (x.nodeType == 1 /* Node.ELEMENT_NODE */) kids.push(x);
10
11 // Now sort the array based on the text content of each kid.
12 // Assume that each kid has only a single child and it is a Text node
13 kids.sort(function(n, m) { // This is the comparator function for sorting
14 var s = n.firstChild.data; // text of node n
15 var t = m.firstChild.data; // text of node m
16 if (s < t) return -1; // n comes before m
17 else if (s > t) return 1; // n comes after m
18 else return 0; // n and m are equal
19 });
20 for(var i = 0; i < kids.length; i++) e.appendChild(kids[i]);
21}
22</script>
23<ul id="list"> <!-- This is the list we'll sort -->
24<li>one<li>two<li>three<li>four <!-- items are not in alphabetical order -->
25</ul>
26<!-- this is the button that sorts the list -->
27<button onclick="sortkids('list')">Sort list</button>
2function sortkids(e) {
3 // This is the element whose children we are going to sort
4 if (typeof e == "string") e = document.getElementById(e);
5
6 // Transfer the element (but not text node) children of e to a real array
7 var kids = [];
8 for(var x = e.firstChild; x != null; x = x.nextSibling)
9 if (x.nodeType == 1 /* Node.ELEMENT_NODE */) kids.push(x);
10
11 // Now sort the array based on the text content of each kid.
12 // Assume that each kid has only a single child and it is a Text node
13 kids.sort(function(n, m) { // This is the comparator function for sorting
14 var s = n.firstChild.data; // text of node n
15 var t = m.firstChild.data; // text of node m
16 if (s < t) return -1; // n comes before m
17 else if (s > t) return 1; // n comes after m
18 else return 0; // n and m are equal
19 });
20 for(var i = 0; i < kids.length; i++) e.appendChild(kids[i]);
21}
22</script>
23<ul id="list"> <!-- This is the list we'll sort -->
24<li>one<li>two<li>three<li>four <!-- items are not in alphabetical order -->
25</ul>
26<!-- this is the button that sorts the list -->
27<button onclick="sortkids('list')">Sort list</button>
→把文档内容转换为大小写
1function upcase(n) {
2 if (n.nodeType == 3 /*Node.TEXT_NODE*/) {
3 // If the node is a Text node, change its text to uppercase.
4 n.data = n.data.toUpperCase();
5 }
6 else {
7 // If the node is not a Text node, loop through its children
8 // and recursively call this function on each child.
9 var kids = n.childNodes;
10 for(var i = 0; i < kids.length; i++) upcase(kids[i]);
11 }
12}
13
2 if (n.nodeType == 3 /*Node.TEXT_NODE*/) {
3 // If the node is a Text node, change its text to uppercase.
4 n.data = n.data.toUpperCase();
5 }
6 else {
7 // If the node is not a Text node, loop through its children
8 // and recursively call this function on each child.
9 var kids = n.childNodes;
10 for(var i = 0; i < kids.length; i++) upcase(kids[i]);
11 }
12}
13
→把一个节点的父节点重定为<b>元素
1<script>
2function embolden(n) {
3 if (typeof n == "string") n = document.getElementById(n); // Lookup node
4 var b = document.createElement("b"); // Create a new <b> element
5 var parent = n.parentNode; // Get the parent of the node
6 parent.replaceChild(b, n); // Replace the node with the <b> tag
7 b.appendChild(n); // Make the node a child of the <b> element
8}
9</script>
10
11<!-- A couple of sample paragraphs -->
12<p id="p1">This <i>is</i> paragraph #1.</p>
13<p id="p2">This <i>is</i> paragraph #2.</p>
14<!-- A button that invokes the embolden() function on the element named p1 -->
15<button onclick="embolden('p1');">Embolden</button>
16
17
2function embolden(n) {
3 if (typeof n == "string") n = document.getElementById(n); // Lookup node
4 var b = document.createElement("b"); // Create a new <b> element
5 var parent = n.parentNode; // Get the parent of the node
6 parent.replaceChild(b, n); // Replace the node with the <b> tag
7 b.appendChild(n); // Make the node a child of the <b> element
8}
9</script>
10
11<!-- A couple of sample paragraphs -->
12<p id="p1">This <i>is</i> paragraph #1.</p>
13<p id="p2">This <i>is</i> paragraph #2.</p>
14<!-- A button that invokes the embolden() function on the element named p1 -->
15<button onclick="embolden('p1');">Embolden</button>
16
17
8.10 修改节点属性
可以调用element.setAttribute()方法来设置一个节点的属性,如:
1var headline = document.getElementById("headline"); // Find named element
2headline.setAttribute("align", "center"); // Set align='center'
3//或者:headline.align = "center"; // Set alignment attribute.
2headline.setAttribute("align", "center"); // Set align='center'
3//或者:headline.align = "center"; // Set alignment attribute.
8.11 Document段
DocumentFragment是一种特殊类型的节点,它自身不出现在文档中,只作为连续节点集合的临时容器,并允许将这些节点作为一个对象来操作。
把一个DocumentFragment插入文档时,插入的不是DocumentFragment本身,而是它所持有的子节点。
1function reverse(n) {
2 // Create an empty DocumentFragment as a temporary container
3 var f = document.createDocumentFragment();
4 while(n.lastChild) f.appendChild(n.lastChild);
5 // Finally, move the children of f all at once back to n, all at once.
6 n.appendChild(f);
7}
2 // Create an empty DocumentFragment as a temporary container
3 var f = document.createDocumentFragment();
4 while(n.lastChild) f.appendChild(n.lastChild);
5 // Finally, move the children of f all at once back to n, all at once.
6 n.appendChild(f);
7}
8.12 给文档添加内容
Document.createElement()方法和Document.createTextNode()方法创建新的Element节点和Text节点。
而方法Node.appendChild()、Node.insertBefore()和Node.replaceChild()可以用来将它们添加到一个文档。
8.13 innerHTML属性
该属性可以用来获得和设置结点中HTML文本。例如:
1var table = document.createElement("table"); // Create the <table> element
2table.border = 1; // Set an attribute
3// Add a Name|Type|Value header to the table
4table.innerHTML = "<tr><th>Name</th><th>Type</th><th>Value</th></tr>";
2table.border = 1; // Set an attribute
3// Add a Name|Type|Value header to the table
4table.innerHTML = "<tr><th>Name</th><th>Type</th><th>Value</th></tr>";