iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 13
7
Modern Web

重新認識 JavaScript系列 第 13

重新認識 JavaScript: Day 13 DOM Node 的建立、刪除與修改

本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。

購書連結 https://www.tenlong.com.tw/products/9789864344130

讓我們再次重新認識 JavaScript!


https://ithelp.ithome.com.tw/upload/images/20171216/20065504Jkp2EsBSe6.png

阿基米德:「給我一個支點,我就能移動地球。」(Give me a place to stand on, and I will move the Earth.)


在先前的介紹中,我們已經理解了 DOM Node 的類型、以及節點之間的查找與關係。
那麼在今天的介紹裡我們將繼續來說明,如何透過 DOM API 來建立新的節點、修改以及刪除節點。


DOM 節點的新增:

  • document.createElement(tagName)

透過 document.createElement() 可以建立一個新的元素:

var newDiv = document.createElement('div');

在建立新的 div 元素 newDiv 後,這時候我們在瀏覽器上還看不到它,直到透過 appendChild()insertBefore()replaceChild() 等方法將新元素加入至指定的位置之後才會顯示。

新建立的 newDiv 我們也可以同時對它指定屬性,如:

newDiv.id = "myNewDiv";
newDiv.className = "box";
  • document.createTextNode()

之前曾介紹過,除了 HTML 元素節點外,還有「文字節點」,那麼 document.createTextNode() 就是用來建立文字節點的方法。

用法很簡單,直接在 document.createTextNode() 加入字串即可。
createElement 一樣的是,新增的 TextNode 在被加入至某個節點前,我們是看不到它的。

var newDiv = document.createElement('div');

// 建立 textNode 文字節點
var textNode = document.createTextNode("Hello world!");

// 透過 newDiv.appendChild 將 textNode 加入至 newDiv
newDiv.appendChild(textNode);
  • document.createDocumentFragment()

在 DOM 規範的所有節點之中,DocumentFragment 算是最特殊的一種,它是一種沒有父層節點的「最小化文件物件」。 可以把它看作是一個輕量化的 Document,用如同標準文件一般的方式來保存「片段的文件結構」。

例如,一開始我們有一個 HTML 的容器元素:

<ul id="myList"></ul>

接著我們透過 document.createDocumentFragment() 來建立 DocumentFragment

// 取得外層容器 myList
var ul = document.getElementById("myList");

// 建立一個 DocumentFragment,可以把它看作一個「虛擬的容器」
var fragment = document.createDocumentFragment();

for (var i = 0; i < 3; i++){
  // 生成新的 li,加入文字後置入 fragment 中。
  let li = document.createElement("li");
  li.appendChild(document.createTextNode("Item " + (i+1)));
  fragment.appendChild(li);
}

// 最後將組合完成的 fragment 放進 ul 容器
ul.appendChild(fragment);

透過操作 DocumentFragment 與直接操作 DOM 最關鍵的區別在於 DocumentFragment 不是真實的 DOM 結構,所以說 DocumentFragment 的變動並不會影響目前的網頁文件,也不會導致回流(reflow)或引起任何影響效能的情況發生。

換言之,當需要進行大量的 DOM 操作時,用 DocumentFragment 的效能會比直接操作 DOM 好很多。

  • document.write()

如果你跟我一樣在多年前 誤入歧途 開始寫 JavaScript 的話,那你應該對 document.write() 這個方法不陌生。

document 物件要將某內容寫入網頁可以用 write() 方法,當瀏覽器讀取頁面,解析到 document.write() 的時候就會停下來,並且將內容輸出,且不只是單純的字串,也可以是 HTML 的標籤。

我們只要將對應的 HTML 字串傳入:

document.write("<h1>Hello World!</h1>");

甚至,如果要新增一個 <script> 標籤,並指定外部 js 資源也是可以的,但要注意的是:

// 寫成這樣, </script> 會變成目前 script 區塊的結束,導致錯誤
document.write("<script type=\"text\javascript\" src=\"file.js\">" + "</script>");
// 為了避免這個問題,要將結尾的標籤改個寫法 <\/script> 就 ok 了
document.write("<script type=\"text\javascript\" src=\"file.js\">" + "<\/script>");

另外,需要特別注意的是,當網頁已經讀取完成後才執行 document.write(),則裡面的內容會完全覆蓋掉目前的網頁:

window.onload = function(){
  document.write("Hello world!");
};

上面範例的 window.onload 表示網頁已載入完成,此時無論網頁原本有什麼內容,都會被 "Hello world!" 所覆蓋。

關於 window.onload 我們後續介紹「事件」的時候會再回來說明。


DOM 節點的修改與刪除

上面介紹了很多建立 DOM 節點的方法,除了最後的 document.write 之外,其他都只是單純建立節點,並未輸出至網頁上。

那麼,接下來要介紹的幾個方法,則說明要如何將剛剛建立好的 DOM 節點,置入到我們所指定的位置。

  • NODE.appendChild(childNode)

https://ithelp.ithome.com.tw/upload/images/20171216/20065504kFU2w5lR4s.png

透過 appendChild() 方法,可以將指定的 childNode 節點,加入到 Node 父容器節點的末端:

<ul id="myList">
  <li>Item 01</li>
  <li>Item 02</li>
  <li>Item 03</li>
</ul>

<script>
  // 取得容器
  var myList = document.getElementById('myList');
  // 建立新的 <li> 元素
  var newList = document.createElement('li');

  // 建立 textNode 文字節點
  var textNode = document.createTextNode("Hello world!");

  // 透過 appendChild 將 textNode 加入至 newList
  newList.appendChild(textNode);

  // 透過 appendChild 將 newList 加入至 myList
  myList.appendChild(newList);
</script>
  • NODE.insertBefore(newNode, refNode);

https://ithelp.ithome.com.tw/upload/images/20171216/200655041z65AVrZ1l.png

insertBefore() 方法,則是將新節點 newNode 插入至指定的 refNode 節點的前面:

<ul id="myList">
  <li>Item 01</li>
  <li>Item 02</li>
  <li>Item 03</li>
</ul>

<script>
  // 取得容器
  var myList  = document.getElementById('myList');

  // 取得 "<li>Item 02</li>" 的元素
  var refNode = document.querySelectorAll('li')[1];

  // 建立 li 元素節點
  var newNode = document.createElement('li');

  // 建立 textNode 文字節點
  var textNode = document.createTextNode("Hello world!");
  newNode.appendChild(textNode);

  // 將新節點 newNode 插入 refNode 的前方
  myList.insertBefore(newNode, refNode);
<script>
  • NODE.replaceChild(newChildNode, oldChildNode)

https://ithelp.ithome.com.tw/upload/images/20171216/20065504njEtKj31Uh.png

replaceChild() 方法,則是將原本的 oldChildNode 替換成指定的 newChildNode

<ul id="myList">
  <li>Item 01</li>
  <li>Item 02</li>
  <li>Item 03</li>
</ul>

<script>
  // 取得容器
  var myList  = document.getElementById('myList');

  // 取得 "<li>Item 02</li>" 的元素
  var oldNode = document.querySelectorAll('li')[1];

  // 建立 li 元素節點
  var newNode = document.createElement('li');

  // 建立 textNode 文字節點
  var textNode = document.createTextNode("Hello world!");
  newNode.appendChild(textNode);

  // 將原有的 oldNode 替換成新節點 newNode
  myList.replaceChild(newNode, oldNode);
<script>
  • NODE.removeChild(childNode)

https://ithelp.ithome.com.tw/upload/images/20171216/20065504orRk1H8B3s.png

removeChild 方法,則是將指定的 childNode 子節點移除。

<ul id="myList">
  <li>Item 01</li>
  <li>Item 02</li>
  <li>Item 03</li>
</ul>

<script>
  // 取得容器
  var myList  = document.getElementById('myList');

  // 取得 "<li>Item 02</li>" 的元素
  var removeNode = document.querySelectorAll('li')[1];

  // 將 myList 下的 removeNode 節點移除
  myList.removeChild(removeNode);
<script>

相信看完今天的分享,已經有能力可以透過 DOM 提供的 API 來進行節點的新增、修改以及刪除了。

大家也許會發現,無論要對網頁元素做出什麼樣的操作,我們都會基於某個節點出發對吧?

剛好呼應一開始阿基米德的名言:

「給我一個支點,我就能移動地球。」

那麼有了 DOM API 的支援後, JavaScript 也可以說:

「給我一個節點,我就能建立整個網頁世界。」


上一篇
重新認識 JavaScript: Day 12 透過 DOM API 查找節點
下一篇
重新認識 JavaScript: Day 14 事件機制的原理
系列文
重新認識 JavaScript37

尚未有邦友留言

立即登入留言