Client-side Javascript的主要目的是腳本化web page內容, 把靜態的HTML轉換成交互式的web應用程序. Scripting Document的主要內容:html
DOM的基本架構;node
怎樣從Document query和select元素(Element);web
怎樣遍歷(Traverse)Document, 怎麼查找任何一個document元素的祖先(ancestors),兄弟(siblings),子孫(descendant);數組
怎樣query和set document元素的屬性;架構
怎樣query,set和modify document的內容;ide
怎樣經過creating,inserting和deleting節點(node)來改變document的結構;oop
HTML form怎樣工做;spa
DOM概要orm
DOM是表示和操做HTML和XML的基本API, HTML和XML文檔的嵌套元素用DOM表示成對像樹,HTML文檔(document)包含表示HTML標籤(tag)和元素(element)的節點(node)和表示文本字符串的節點(node), HTML document也包含表示聲明(comments)的節點.server
<html> <head> <title>Sample Document</title> </head> <body> <h1>An HTML Document</h1> <p>This is a <i>simple</i> document. </html>
從上圖樹的根是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,<iframe>,<img>. 若根據name屬性查找元素, 能夠使用文檔對象的getElementsByName()方法, 如:
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()返回的元素順序是元素在文檔中順序, 好比在文檔中查找第一個<p>元素能夠用:
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); } }