Client-side Javascript的主要目的是脚本化web page内容, 把静态的HTML转换成交互式的web应用程序. Scripting Document的主要内容:
DOM的基本架构;
怎样从Document query和select元素(Element);
怎样遍历(Traverse)Document, 怎么查找任何一个document元素的祖先(ancestors),兄弟(siblings),子孙(descendant);
怎样query和set document元素的属性;
怎样query,set和modify document的内容;
怎样通过creating,inserting和deleting节点(node)来改变document的结构;
HTML form怎样工作;
DOM概要
DOM是表示和操作HTML和XML的基本API, HTML和XML文档的嵌套元素用DOM表示成对像树,HTML文档(document)包含表示HTML标签(tag)和元素(element)的节点(node)和表示文本字符串的节点(node), HTML document也包含表示声明(comments)的节点.
Sample Document An HTML Document
This is a simple document.
从上图树的根是Document Node,表示整个文档; 表示HTML元素的节点是HTML Node; 表示文本的是Text Node, Document, Element和Text是Node的子类.
从上面的类层次图,需要注意的是Document和Element,HTMLDocument和HTMLElement之间是有区别的, Document要么表示HTML文档, 要么表示XML文档, 而Element则表示Document的元素. HTMLDocument和HTMLElement都是Document和Element的子类.
2. 查找Document元素
Client-side Javascript程序从某种程度来说就是操作一个或多个元素, 当程序启动后,使用全局变量document引用文档对象. 为了操作文档元素, 需要查找要操作的元素, DOM定义如下方法来查找元素:
使用指定的属性id;
使用指定的属性name;
使用指定的标签name;
使用指定的CSS class;
匹配指定的CSS选择器;
2.1 通过属性ID查找元素
任何HTML元素都有一个在整个文档唯一的id属性, 因此可以通过使用文档对象的getElementById()来查找元素. getElementById()方法只能查找一个元素,如果需要查找多个元素,可以使用如下的方法:
function getElements(/*ids...*/) { var elements = {}; for(var i = 0; i < arguments.length; i++) { var id = arguments[i]; var elt = document.getElementById(id); if (elt == null) throw new Error("No element with id: " + id); elements[id] = elt; } return elements; }
2.2 通过属性Name查找元素
HTML name属性最初是为了给form元素设定name, 而且name属性只有在form数据在提交给server时才被使用, 需要注意的是name属性值是不唯一的,比如在form中的radio按钮和checkboxes. 还有name属性只有在少部分元素上是合法的, 比如form,
var radiobuttons = document.getElementsByName("favorite_color");
getElementsByName()通过HTMLDocument类定义,而不是Document类, 所以只能在HTML文档上使用,不能在XML文档上使用, 该方法返回一个read-only的元素对象数组.
2.3 通过属性Type查找元素
通过文档对象的getElementsByType()方法可以查找指定Type的所有HTML和XML元素, 该方法返回一个read-only的元素对象数组. 如:
var spans = document.getElementsByTagName("span");
getElememtsByTagName()返回的元素顺序是元素在文档中顺序, 比如在文档中查找第一个
元素可以用:
var firstpara = document.getElementsByTagName("p")[0];
HTML tag是大小写不明感的, 当用getElememtsByTagName()时,会忽略大小进行name的比较. 也可以在getElememtsByTagName()中使用通配符*查找所有元素. 除了Document类定义了getElememtsByTagName(), Element类也定义了getElememtsByTagName(),其使用跟Document的一样,只是getElememtsByTagName()只返回调用它的元素的子孙. 如:
var firstpara = document.getElementsByTagName("p")[0]; var firstParaSpans = firstpara.getElementsByTagName("span");
2.4 通过CSS class查找元素
HTML的class属性是以空格分开的一个或多个标识符, 同getElememtsByTagName()一样,getElementsByClass()可以被HTML document和HTML element调用, 返回匹配的class的read-only的所有子孙. 需注意的是getElementsByClass()方法以空格分隔class标识符, 而不是逗号.如:
var warnings = document.getElementsByClassName("warning"); var log = document.getElementById("log"); var fatal = log.getElementsByClassName("fatal error");
2.5 通过CSS选择器查找元素
CSS stylesheets使用Selector描述文档中的元素和元素集.
元素可以通过ID, tag name或者class来描述, 如: #nav, div, .warning
元素可以通过属性值查找, 如: p[lang="fr"] *[name="x"]
基本选择器可以被合并, 如: span.fatal.error span[lang="fr"].warning
选择器也可以指定文档结构, 如: #log span #log>span body>h1:first-child
可以合并选择器来查找多个元素和元素集, 如: div, #log
W3C定义了标准的API querySelectorAll()来查找指定选择器的元素, 该方法返回元素不像上述查找的元素,是not live的, 也就是返回的Nodelist保存该方法调用时匹配的元素, 当文档结构发生变化时不会更新. 如果该方法没有查到匹配的元素, 返回空的Nodelist, 如果发生错误, 抛出异常错误. 除了querySelectorAll(), document也定义了querySelector(), 使用方法同querySelectorAll(),但该方法只返回第一个匹配的元素, 如果没有匹配的, 则返回null. 这两个方法也定义在Element类上, 但只返回调用该方法元素的子孙.
3. 文档结构和遍历
3.1 作为节点树的文档
parentNode:
当前节点的父节点,Document节点的父节点是null, 因为它没有父节点.
childNodes:
一个read-only的类数组对象.
firstChild, lastChild:
一个节点的第一孩子节点和最后孩子节点, null表示没有孩子.
nextSibling, previousSibling:
一个节点的下一个和前一个兄弟节点, 两个节点拥有共同的父节点.
nodeType:
节点的类型, Document节点的值9, Element节点的值是1, 文本节点的值是3, 声明节点的值是8, Document Faragment是11.
nodeValue:
文本和声明节点的文本内容.
nodeName:
元素的tag名字,转换成大写.
3.2 作为元素树的文档
Element对象的属性children只返回元素对象.
Text和Comment节点没有孩子, 这意味着Node.parentNode不会返回Text和Comment节点.
基于元素的文档遍历API是Element属性, 其等价于Node对象的child和sibling属性.
firstElementChild, lastElementChild
nextElementSibling, previousElementSibling
clildElementCount
function parent(e, n) { if (n === undefined) n = 1; while(n-- && e) e = e.parentNode; if (!e || e.nodeType !== 1) return null; return e; } function sibling(e,n) { while(e && n !== 0) { if (n > 0) { if (e.nextElementSibling) e = e.nextElementSibling; else { for(e=e.nextSibling; e && e.nodeType !== 1; e=e.nextSibling) /* empty loop */ ; } n--; } else { if (e.previousElementSibing) e = e.previousElementSibling; else { for(e=e.previousSibling; e&&e.nodeType!==1; e=e.previousSibling) /* empty loop */ ; } n++; } } return e; } function child(e, n) { if (e.children) { if (n < 0) n += e.children.length; if (n < 0) return null; return e.children[n]; } if (n >= 0) { if (e.firstElementChild) e = e.firstElementChild; else { for(e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling) /* empty */; } return sibling(e, n); } else { if (e.lastElementChild) e = e.lastElementChild; else { for(e = e.lastChild; e && e.nodeType !== 1; e=e.previousSibling) /* empty */; } return sibling(e, n+1); } }