1. 程式人生 > >操作DOM

操作DOM

ron cnblogs code 關心 例如 log 版本 com rst

由於HTML文檔被瀏覽器解析後就是一顆DOM樹,要改變HTML的結構,就需要通過JavaScript來操作DOM。

始終記住DOM是一個樹形結構。操作一個DOM節點實際上就是這麽幾個操作:

更新:更新該DOM節點的內容,相當於更新了該DOM節點表示的HTML的內容。

遍歷:遍歷該DOM節點下的子節點。以便於進一步操作;

添加:在該DOM節點下新增一個子節點,相當於動態增加了一個HTML節點;

刪除:將該節點從HTML中刪除。相當於刪除掉了該DOM節點的內容以及它包含的所有子節點。

在操作DOM節點前,我們需要通過各種方式先拿到這個DOM節點。最常用的方法是document.getElementById()和document.getElementsByTagName(),以及CSS選擇器document.getElementsByClassName();

由於ID在HTML文檔中是唯一的,所以document.getElementById()可以直接定位唯一的一個DOM節點。document.getElementsByTagName()和document.getElementsByClassName()總是返回一組DOM節點。要精確地選擇DOM,可以先定位父節點,再從父節點開始選擇,以縮小範圍。

    //返回ID為’test‘的節點
        var test = document.getElementById(‘test‘);

        //先定位ID為’test-table‘的節點,再返回其內部所有tr節點
        var
trs = document.getElementById(‘test-table‘).getElementsByTagName(‘tr‘); //先定位ID為’test-div‘的節點,再返回其內部所有class包含red的節點。 var reds = document.getElementById(‘test-div‘).getElementsByClassName(‘red‘); //獲取節點test下的所有直屬子節點: var cs = test.children; //獲取節點test下第一個、最後一個子節點:
var first = test.firstElementChild; var last = test.lastElementChild;

第二種方法是使用querySelector()和querySelectorAll(),需要了解selector語法,然後使用條件來獲取節點,更加方便:

     //通過querySelector獲取ID為q1的節點:
        var q1 = document.querySelector(‘#q1‘);

        //通過querySelectorAll獲取q1節點內的class為highlighted的div的所有子元素p:
        var ps = q1.querySelectorAll(‘div.highlighted >p‘);

低版本的IE<8不支持querySelectorAll和querySelector,IE8有的支持。

嚴格來講,我們這裏的DOM節點是指Element,但是DOM節點實際上是Node,在HTML中,Node包括Element、Comment、CDATA_SECTION等很多種,以及根節點Document類型,但是,絕大多數時候我們只關心Element,也就是實際控制頁面結構的Node,其他類型的Node忽略即可。根節點Document已自動綁定為全局變量document

更新DOM

拿到一個DOM節點後,我們可以對它進行更新。

可以直接修改節點的文本,方法有兩種

一是修改innerHTML屬性,這個方式非常強大,不但可以修改一個DOM節點的文本內容,還可以直接通過HTML片段修改DOM節點內部的子樹。

     //獲取<p id="p-id">...</p>
        var p = document.getElementById("p-id");
        //設置文本為abc
        p.innerHTML=‘ABC‘;//<p id="p-id">ABC</p>
        //設置HTML:
        p.innerHTML=‘ABC<span style="color:red">RED</span>XYZ‘;//<p>...</p>的內部結構已修改。

用innerHTML時要註意,是否需要寫入HTML。如果寫入的字符串是通過網絡拿到,要註意對字符編碼來比嗎XSS共計。

第二種是修改innerText或者textContent屬性,這樣可以自動對字符串進行HTML編碼,保證無法設置任何HTML標簽:

// 獲取<p id="p-id">...</p>
var p = document.getElementById(‘p-id‘);
// 設置文本:
p.innerText = ‘<script>alert("Hi")</script>‘;
// HTML被自動編碼,無法設置一個<script>節點:
// <p id="p-id">&lt;script&gt;alert("Hi")&lt;/script&gt;</p>

兩者的區別在於讀取屬性時,innerText不返回隱藏元素的文本,而textContent返回所有文本。另外註意IE<9不支持textConten

修改CSS也是經常需要的操作。DOM節點的style屬性對應所有的CSS,可以直接獲取或者設置。因為css允許font-size這樣的名稱,但它並非JavaScript有效的屬性名,所以需要在JavaScript中改寫為駝峰式命名fontSize:

// 獲取<p id="p-id">...</p>
var p = document.getElementById(‘p-id‘);
// 設置CSS:
p.style.color = ‘#ff0000‘;
p.style.fontSize = ‘20px‘;
p.style.paddingTop = ‘2em‘;

插入DOM

當我們獲得了某個DOM節點,想在這個DOM節點內插入新的DOM,應該如何做?

如果這個DOM節點是空的,例如,<div></div>,那麽,直接使用innerHTML=‘<span>child</span>’就可以修改DOM節點的內容,相當於“插入”了新的DOM節點。

如果這個DOM節點不是空的,那就不能這麽做,因為innerHTML會直接替換掉原來的所有子節點。

有兩個辦法可以插入新的節點,一個是使用appendChild,把一個子節點添加到父節點的最後一個子節點。

HTML結構:

  <p id="js">JavaScript</p>
    <div id="list">
        <p id="java">Java</p>
        <p id="python">python</p>
        <p id="scheme">scheme</p>
    </div>

把<p id="js">javascript</p>添加到<div id="list">的最後一項:

      var js = document.getElementById(‘js‘),
        list = document.getElementById(‘list‘);
        list.appendChild(js);

現在html結構變成這樣:

  
    <div id="list">
        <p id="java">Java</p>
        <p id="python">python</p>
        <p id="scheme">scheme</p>
     <p id="js">JavaScript</p> </div>

因為我們插入的js節點已經存在於當前的文檔樹,因此這個節點首先會從原來的位置刪除,再插入到新的位置。

更多的時候我們會從零創建一個新的節點,然後插入到指定位置:

      var list = document.getElementById(‘list‘),
        haskell = document.createElement(‘p‘);
        haskell.id = ‘haskell‘;
        haskell.innerText = ‘Haskell‘;
        list.appendChild(haskell);

這樣我們就動態添加了一個新的節點:

  <div id="list">
        <p id="java">Java</p>
        <p id="python">python</p>
        <p id="scheme">scheme</p>
        <p id="haskell">Haskell</p>
    </div>

動態創建一個節點然後添加到DOM樹種,可以實現很多功能,舉個例子,下面的代碼動態地創建了一個<style>節點,然後把它添加到<head>節點的末尾,這樣就動態地給文檔添加了新的css定義:

     var d = documnet.createElement(‘style‘);
        d.setAttribute(‘type‘,‘text/css‘);
        d.innerHTML=‘p{color:red}‘;
        document.getElementsByTagName(‘head‘)[0].appendChild(d);

inertBefore

如果我們要把子節點插入到指定的位置怎麽辦?可以使用parentElement.insertBefore(newElement, referenceElement);

子節點會插入到referenceElement之前。

假如我們要把Haskell插入到python之前:

     var list = document.getElementsById(‘list‘),
        ref = document.getElementById(‘python‘),
        haskell = document.createElement(‘p‘);
        haskell.id =‘haskell‘;
        haskell.innerText = ‘Haskell‘;
        list.insertBefore(haskell,ref);

新的HTML結構:

<div id="list">
    <p id="java">Java</p>
    <p id="haskell">Haskell</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

使用insetBefore重點是要拿到一個“參考子節點”的引用。很多時候,需要循環一個父節點的所有子節點,可以通過叠代children屬性實現:

     var i,c,list = document.getElementById(‘list‘);
        for(i=0;i<list.children.length;i++){
            c=list.children[i];//拿到第i個子節點
        }

刪除DOM

刪除一個DOM節點就比插入要容易多。

要刪除一個節點,首先要獲得該節點本身以及它的父節點,然後,調用父節點的removeChild把自己刪掉

    //拿到待刪除的節點
        var self = document.getElementById(‘to-be-removed‘);
        //拿到父節點
        var parent = self.parentElement;
        //刪除
        var removed = parent.removeChile(self);
        removed === self;//true

註意到刪除後的節點雖然不在文檔樹中了,但其實它還在內存中,可以隨時再次被添加到別的位置。

當你遍歷一個父節點的子節點並進行刪除操作時,要註意,children屬性是一個只讀屬性,並且它的子節點變化時會實時更新

例如對於以下HTML

<div id="parent">
    <p>First</p>
    <p>Second</p>
</div>

當我們用如下代碼刪除子節點時:

var parent = document.getElementById(‘parent‘);
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 瀏覽器報錯

瀏覽器報錯:parent.children[1]不是一個有效的節點。原因就在於,當<p>First</p>節點被刪除後,parent.children的節點數量已經從2變為了1,索引[1]已經不存在了。

因此,刪除多個節點時,要註意children屬性時刻都在變化

操作DOM