JavaScript_DOM編程藝術第二版學習筆記-第7章

動態創建標記

傳統技術:document.write和innerHTML
深入剖析DOM方法:createElement、createTextNode、appendChild和insertBefore。

一、傳統方法

1.1 document.write
利用document.write可以在頁面上寫入內容,但它有着一個很大的缺點就是它違背了“行爲與表現分離”的原則。即使將document.write語句挪到外部函數裏,也還是需要在標記的body部分使用script標籤才能調用那個函數。而將JS和HTML代碼混雜在一起既不易閱讀和編輯,也無法享受到行爲與結構分離的好處。
這樣混雜的文檔容易導致驗證錯誤。還有,MIME類型application/xhtml+xml與document.write不兼容,瀏覽器在呈現這種XHTML文檔時根本不會執行document.write方法。從某種意義上講,使用document.write方法有點像使用font標籤去設定字體和顏色,雖然在HTML裏運行的不錯,但不夠優雅。
1.2 innerHTML屬性
現如今的瀏覽器幾乎都支持innerHTML屬性,它並不是W3C DOM標準的組成部分,但已被包含在HTML5規範中。
對於如下testdiv例子:

<div id="testdiv">
    <p>This is <em>my</em> content.</p>
</div>

DOM的視角DOM的視角
DOM提供了關於這個標記非常詳細的一幅圖畫,使用DOM方法和屬性可以對任何一個節點進行單獨的訪問。同樣的標記從innerHtml屬性的角度看則簡單很多,如下:
innerHtml視角這裏寫圖片描述
很顯然,innerHtml屬性毫無細節可言。要想獲得細節就必須使用DOM方法和屬性。標準化的DOM像手術刀一樣精細,innerHtml屬性就象一把大錘那樣粗放。當然,如果需要把一大段HTML內容插入網頁時,innerHtml更適用。它既支持讀取,又支持寫入。

<div id="testdiv">
</div>
window.onload = function(){
    var testdiv = document.getElementById("testdiv");
    testdiv.innerHTML = "<p>This is <em>my</em> content.</p>";
}

一樣可以在頁面上寫下內容:This is my content.使用innerHTML,頁面的內容將全都被替換。
innerHTML屬性要比document.write()方法更值得推薦,使用innerHTML屬性,可以把JS代碼從標記中分離出來。用不着再在標記的body部分插入script標籤。與document.write()方法類似,瀏覽器在呈現XHTML文檔時會直接忽略掉innerHTML屬性。

二、DOM方法

DOM是文檔的表示,DOM所包含的信息與文檔裏的信息一一對應,只要學會問正確的問題,使用正確的方法,就可以獲取DOM節點樹上的任何一個節點的細節。瀏覽器實際顯示的是DOM節點樹。明白這個道理,以動態方式創建標記就不那麼難以理解了。動態創建標記實則是在改變DOM節點樹,從DOM的角度去思考問題。
在DOM看來,一個文檔就是一顆節點樹。如果想在節點樹上添加內容,就必須插入新的節點;如果想添加一些標記,就必須插入元素節點。

2.1 createElement方法

繼續使用第一小節的testdiv例子,我們將div設置爲空的內容,然後通過動態創建標籤的方式寫入內容。期望:在div內寫入一段文本,內容爲:This is my content.

這項任務分兩步驟:
1.創建一個新元素
2.把這個元素插入到DOM節點樹

首先,我們需要創建標籤元素p,用到DOM的createElement方法,createElement的語法是:
document.createElement(nodeName)
下面創建段落標籤:document.createElement("p")。這樣一個p標籤就被創建成功了。
不論何時何地,只要使用了createElement方法,就該把新創建出來的元素附給一個變量:var para = document.createElement("p");
變量para包含着一個指向剛創建出來的p元素的引用,現在新創建出來的元素節點,擁有了自身的DOM屬性,但是不在DOM樹的任何一個節點上,遊蕩在JavaScript世界的一個孤兒。

2.2 appendChild方法

把新創建的節點插入某個文檔的節點樹最簡單的方法就是,讓這個新節點成爲某個現有節點的子節點。具體到testdiv的例子,我們應該讓新建的P標籤成爲div的子節點。這裏我們用到appendChild方法,語法是:parent.appendChild(child)
這裏我們應該先獲取父節點,使用getElementById()的DOM方法,即:
var testdiv = document.getElementById("testdiv"),同樣的,我們把查詢值賦值給一個變量,方便後面的調用。
有了父節點,事情就好辦多了。實現將P節點綁定到testdiv文檔中的完整代碼如下:

var para = document.createElement("p");
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);
2.3 createTextNode 方法

到這步爲止,我們已經創建出了一個元素節點並插入到了文檔的節點樹中,這個節點目前沒有任何內容即沒有子節點,但是之前的createElement方法只能創建元素節點,並不能創建內容。這裏用createTextNode方法來實現它。
createTextNode方法的語法與createElement方法的語法差不多:document.createTextNode("text");
下面是實現創建一個內容爲Hello world的文本節點:

var text = document.createTextNode("Hello world");

同樣的,現在我們已經創建了一個指向文本內容爲”Hello world”文本節點的變量,這個文本節點也不在DOM樹節點中,所以我們需要將它成爲某個DOM節點的子節點,使得它成爲DOM樹中一員。這裏我們把文本節點綁定到p標籤:

var text = document.createTextNode("Hello world");
var para = document.createElement("p");
para.appendChild(text);

到目前爲止,我們實現了在頁面上有內容顯示。完整代碼如下:

var text = document.createTextNode("Hello world");
var para = document.createElement("p");
para.appendChild(text);
var testdiv = document.getElementById("testdiv");
testdiv.appendChild(para);

三、進階

3.1 在已有元素前插入一個新元素

insertBefore()方法將把一個新元素插入到一個現有元素的前面,調用此方法時,必須知道三件事:

  • 新元素(newElement):你想插入的元素
  • 目標元素(targetElement):你想插入到那個元素之前
  • 父元素(parentElement):目標元素的父元素
    parentElement.insertBefore(newElement,targetElement)
    這裏父元素可以用parentNode屬性值獲取。如下,在gallery前面插入placeholder圖片:
    gallery.parentNode.insertBefore(placeholder,gallery)
3.2 在已有方法後插入一個新元素

可能你會想,既然有一個insertBefore方法,是否有insertAfter方法,可惜DOM沒有提供這個方法。我們依然可以編寫實現這個方法。
1.編寫insertAfter函數
我們可以用已有的DOM方法和屬性編寫一個insertAfter函數:

function insertAfter(newElement,targetElement){
    var parent = targetElement.parentNode;//parentElement父元素
    if (parent.lastChild == targetElement){
        parent.appendChild(newElement);
        //如果目標元素是父元素的最後一個子元素,則將新元素成爲父元素的子節點(追加到父元素上),按順序則成爲新的最後一個子節點,即成功插入在目標元素之後
    }
    else{
        parent.insertBefore(newElement,targetElement.nextSibling);
        //如果目標元素不是父元素的最後一個子元素,則將新元素插入到目標元素緊挨着的下一個兄弟元素之前。
    }
}

這些函數用到了以下的DOM方法和屬性:

  • parentNode屬性
  • lastChild屬性
  • appendChild方法
  • insertBefore方法
  • nextSibling屬性

四、圖片庫優化

利用上面的知識,我們將圖片庫的結構、樣式和行爲徹底分離。JS代碼增加了,但標記相應的減少了。圖片庫文件現在只包含一個由JS腳本和CSS樣式表共用的“掛鉤”。這個“掛鉤”就是圖片清單的id屬性。實現效果以及完整代碼如下:
圖片庫效果

JS代碼

function addLoadEvent(func){
    var oldonload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = func;
    } else{
        window.onload = function(){
            oldonload();
            func();
        }
    }
}
function insertAfter(){
    var parent = targetElement.parentNode;//parentElement
    if (parent.lastChild == targetElement){
        parent.appendChild(newElement);
        //如果目標元素是父元素的最後一個子元素,則將新元素成爲父元素的子節點,按順序則成爲新的最後一個子節點,即成功插入在目標元素之後
    }
    else{
        parent.insertBefore(newElement,targetElement.nextSibling);
        //如果目標元素不是父元素的最後一個子元素,則將新元素插入到目標元素緊挨着的下一個兄弟節點之前。
    }
}
function preparePlaceholder(){
    if (!document.createElement) return false;
    if (!document.createTextNode) return false;
    if (!document.getElementById) return false; 
    if (!document.getElementById('imageallery')) return false;  
    var placeholder = document.createElement('img');
    placeholder.setAttribute("id","placeholder");
    placeholder.setAttribute("src","./../images/b.jpg");
    placeholder.setAttribute("alt","my image");

    var description = document.createElement("p");
    description.setAttribute("id","description");

    var desctext = document.createTextNode("choose an image");
    description.appendChild(desctext);

    var gallery = document.getElementById("imageallery");
    insertAfter(placeholder,gallery);
    insertAfter(description,placeholder);
}

function prepareGallery() {
    if (!document.getElementsByTagName) return false;
    if (!document.getElementById) return false;
    if (!document.getElementById('imageallery')) return false;
    var gallery = document.getElementById('imageallery');
    var links = gallery.getElementsByTagName('a');
    for (var i = 0; i < links.length; i++) {
        links[i].onclick = function() {
            return !showPic(this);
        }
        links[i].onkeypress = links[i].onclick;//對於鍵盤輸入等同於鼠標事件
    };
}

function showPic(whichpic) {
    if (!document.getElementById('placeholder')) return false;
    var source = whichpic.getAttribute("href");
    var placeholder = document.getElementById('placeholder');
    placeholder.setAttribute("src", source);
    // 檢查placeholder元素是否存在
    if (placeholder.nodeName != "IMG") return false;    
    if (document.getElementById('description')) {
        var text = whichpic.getAttribute("title") ? whichpic.getAttribute("title") : "";
        var description = document.getElementById("description");
        if (description.firstChild.nodeType == 3) {
            description.firstChild.nodeValue = text;
        }
    }
    return false;
}
addLoadEvent(preparePlaceholder);
addLoadEvent(prepareGallery);

CSS代碼

body{
    font-family: "Helvetica","Arial",serif;
    color: #333;
    background-color: #ccc;
    margin: 1em 10%;
}
h1{
    color: #333;
    background-color: transparent;
}
a{
    color: #c60;
    background-color: transparent;
    font-weight: bold;
    text-decoration: none;
}
ul{
    padding: 0;
}
li{
    float: left;
    padding: 1em;
    list-style: none;
}
img{
    display: block;
    clear: both;
    width:80px;
    height:60px;
}
#placeholder{
    width: 400px;
    height: 400px;
}

html代碼

<html>

<head>
    <meta charset=utf-8>
    <title>圖片庫</title>
    <link rel="stylesheet" type="text/css" href="./../styles/showPic.css">
</head>
<body>
    <h1>圖片庫</h1>
    <ul id="imageallery">
        <li>
            <a href="./../images/11.jpg" onclick="showPic(this); return false;" title="狗狗">
                <img src="./../images/11.jpg">
            </a>
        </li>
        <li>
            <a href="./../images/22.jpg" onclick="showPic(this); return false;" title="花朵">
                <img src="./../images/22.jpg">
            </a>
        </li>
        <li>
            <a href="./../images/a.jpg" onclick="showPic(this); return false;" title="手套">
               <img src="./../images/a.jpg">
            </a>
        </li>
        <li>
            <a href="./../images/border.png" onclick="showPic(this); return false;" title="邊框">
                <img src="./../images/border.png">                
            </a>
        </li>
    </ul>
    <img id="placeholder" src="./../images/b.jpg" alt="my image">
    <p id="description">chooese an image</p>
    <script src="./../javascripts/showPic.js"></script>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章