React 寫久了,對於 DOM 節點的操作變的好不熟悉啊! :scream_cat: 最近收到一個 Javascript 的前端面試題,剛好複習 DOM 節點的操作。 ## 在這之前,先了解 DOM 是什麼? DOM 的全稱是 Document Object Model 文件物件模型,是一個像樹狀圖的文件結構。 ![](https://hackmd.io/_uploads/S1XLYjY_h.png) 當瀏覽器讀取文件檔案,會按照形成 DOM tree 的結構節點去渲染出畫面,並定義讓程式可以存取並改變文件架構、風格和內容的方法。 也就是說,我們可以利用 JS 的方法去查看存取渲染樹中的節點,去新增、刪除、修改、替換其他節點(元素)、去添加 class 更改呈現樣式,也可以附加事件處理程序,一旦觸發事件就會執行處理程序。 最常用的使用方式是配合使用者的互動去改動節點。 先來看看語法,我們再來看如何使用會更有感覺~ ## 有哪些 DOM 操作使用方法? ### 找到節點 Document 的方法,先來介紹 **querySelector 和 querySelectorAll** querySelector 很好用,可以去幫我們找到特定的節點,不管你要找 class, id, 還是特定的節點都可以。 如果真的找不到指定節點,他就會回傳 null。 **querySelector(selectors)** ``` <div id="box"></div> <div class="box"></div> <p> Hi </p> // 複雜版本的節點 <div class="complex main"> <input type="checkbox"/> </div> <script> const idElement = document.querySelector("#box") const classElement= document.querySelector(".box") const pElement = document.querySelector("p") const complexElement = document.querySelector("div.complex.main input[type='checkbox']") </script> ``` 至於 querySelectorAll,就如字面上來說,幫助你快速抓到所有節點的好幫手! **querySelectorAll(selectors)** ``` <section id="box-wrapper"> <div class="box">div <br>1</div> <div class="box">div <br>2</div> <div class="box">div <br>3</div> <div class="box">div <br>4</div> </section> <script> const boxNodes = document.querySelectorAll('.box') </script> ``` **繼續用別的方法來找到 HTML 元素** | 方法 | 描述 | | ---- | ---- | | document.getElementById(id) | 用 id 找到元素 | | document.getElementsByTagName(name) | 用標籤名稱找到元素| | document.getElementsByClassName(name) | 用 class 找到元素 | **改變 HTML 的元素** | 屬性 | 描述 | | -------- | -------- | | element.innerHTML = new html content | 改變元素裡面的 HTML | | element.attribute = new value | 改變 HTML 元素裡的屬性值 | | element.style.property = new style | 改變 HTML 元素的 style | | 方法 | 描述 | | -------- | -------- | | element.setAttribute(attribute, value) |改變 HTML 元素裡的屬性值 | **增加或刪除元素** | 方法 | 描述 | | ------------------------------- | ---- | | document.createElement(element) | 建立一個新的 html 元素| | document.removeChild(element) | 移除一個 html 元素 | | document.appendChild(element) | 加入一個 html 元素 | | document.replaceChild(new, old) | 替換一個 html 元素 | | document.insertBefore(new, original) | 插入一個元素在指定節點前 | 來用上面的 <selection></selection> box html 檔案來組合一下,如何用上方的方法來操作 DOM 節點。 ``` const boxWrapper = document.getElementById("box-wrapper"); const box = document.createElement("div"); box.innerHTML = 'div <br> 5'; box.style.backgroundColor = "orange"; box.classList.add("box"); boxWrapper.appendChild(box); ``` **要注意 appendChild() 和 insertBefore() 加入節點的位置會再不一樣的地方** Node: appendChild() method: 會加在父元素的最後一個節點 Node: insertBefore() method: 可將元素插入在父元素的某個節點前面 如果要做些操作判斷,也可以用 contains() 的方法判斷裡面有沒有特定的 class **element.classList.contains(className)** ## 加上 Event Listener 然後重點來了,**使用者互動要增加的 Event Handlers 來了!** 不過雖然這樣說,事件的發生要注意除了使用者互動,也有可能是來自 API 處理非同步任務所產生的。 只是這邊筆記會只講由使用者操作行為所產生的~ 直接來兩種寫法 :arrow_down: **document.getElementById(id).onclick = function(){code}** **EventTarget.addEventListener(事件, 執行函式 )** 選取節點以後,使用 addEventListener 綁定事件 document.querySelector('#newOptions').addEventListener('change', selectedFuc); 要看 selectedFun 這個函式做什麼,可以繼續往下看我使用 join() 的說明有用到 (就不占版面了!) **小補充:** 講到事件觸發,就要記得這兩個常用的~ 因為我今天差點忘了要加這個 Event.preventDefault() :laughing: **Event.preventDefault():** 是可以取消一些預設行為發生,例如表格裡的提交會自動刷新頁面,我今天的用法是想要按 submit 以後直接在同個頁面上取得資料來顯示,這時候就不能給它刷新了。 **Event.stopPropagation():** 是阻止事件的物件繼續捕捉或冒泡傳遞。 --- 我在這次的問題用到 join() 它要用來幹嘛呢? join() 方法會將陣列(或一個類陣列(array-like)物件)中所有的元素連接、合併成一個字串,並回傳此字串。 說明一下使用情境: 我在所有選項上綁定 change 事件,勾選起來的選項,使用 value 空陣列都把它們推進去,如果取消勾選的就被篩出來。 最後再把它的值 map 出來,但我要把這個類陣列的元素都連接,才可以把它放進去,改變元素裡的 html 。 ``` function selectedFuc(e) { if (e.target.checked) { const option = e.target.value; value.push(option); } else { const option = e.target.value; value = value.filter((item) => item !== option); } // 要將 value 的值動態呈現在 selectedContainer 的 input 裡 const items = document.querySelector('.boxWrapper'); const selectedItems = value.map( (item) => ` <span>${item}</span> ` ); console.log('selectedItems', selectedItems); const test = selectedItems.join(''); console.log('test', test); items.innerHTML = selectedItems.join(''); } ``` 可以看以下截圖比較 selectedItems 和 test 來看使用 Array.prototype.join() 的差別 ![](https://hackmd.io/_uploads/Syn9kOYd3.png) ### 參考資料: w3schools 、 MDN [JavaScript HTML DOM Document](https://www.w3schools.com/js/js_htmldom_document.asp)