※ 本文為 dinos 轉寄自 ptt.cc 更新時間: 2017-04-15 02:47:16
看板 Ajax
作者 標題 [心得] 都2017年了 學學用原生JS來操作DOM吧
時間 Fri Apr 7 19:08:27 2017
JavaScript 在經過這幾年的進化之後,
原本大家習慣使用第三方函示庫(例如 jQuery)包裝的 DOM 操作方法,
現在都能夠使用原生的 JavaScript 來達成了。
參考:https://www.sitepoint.com/dom-manipulation-vanilla-javascript-no-jquery/
The Basics of DOM Manipulation in Vanilla JavaScript (No jQuery) — SitePoint
Sebastian Seitz gives you a crash course in DOM manipulation with vanilla JavaScript, abstracting the more verbose parts into a set of helper function ...
Sebastian Seitz gives you a crash course in DOM manipulation with vanilla JavaScript, abstracting the more verbose parts into a set of helper function ...
【一、查詢和取得 DOM】
我們有很方便的 querySelector() 和 querySelectorAll() 方式來取得 DOM。
// 取得單一元素
const oneElement = document.querySelector('#foo > div.bar')
// 取得所有符合的元素
const allElements = document.querySelectorAll('.bar')
可以透過 matches() 方式檢查元素是否符合指定的選擇器。
oneElement.matches('div.bar') === true
也可以在特定的元素底下繼續查詢。
const button = allElements.querySelector('button[type="submit"]')
那以前慣用的 getElementById()、getElementsByTagName() 呢?
當然也可以使用,但是 querySelector 不能動態更新查詢到的元素。
const elementsNew = document.querySelectorAll('div')
const elementsOld = document.getElementsByTagName('div')
// 動態插入一個新的 div
const newDiv = document.createElement('div')
document.body.appendChild(newDiv)
// elementsOld 會拿到 newDiv;elementsNew 則否。
elementsNew.length !== elementsOld.length
把 querySelectorAll() 回傳的 NodeList 轉成 Array 之後,
就能用 forEach() 方式走訪每個元素。
Array.from(allElements).forEach(element => {
// do something...
})
// IE 還不支援 Array.from(),可以用:
Array.prototype.forEach.call(allElements, element => {
// do something...
})
// 更短的寫法:
[].forEach.call(allElements, element => {
// do something...
})
【二、修改 class 和屬性】
要修改元素的 class,可以用方便的 classList 操作。
oneElement.classList.add('baz')
oneElement.classList.remove('baz')
oneElement.classList.toggle('baz')
// 檢查是否有指定的 class
oneElement.classList.contains('baz')
要修改元素的屬性(attribute),直接指定給該元素即可。
// 取得屬性
const oneValue = oneElement.value
// 設定屬性
oneElement.value = 'hello'
// 一口氣設定好多種屬性,用 Object.assign()
Object.assign(oneElement. {
value: 'hello',
id: 'world'
})
// 要刪除屬性,設定成 null 就好
oneElement.value = null
等等,那為何不用 getAttribute()、setAttribute() 和 removeAttribute () 呢?
因為這些方式是直接修改 HTML 的屬性,會導致瀏覽器進行重繪(redraw),
對效能來說是很大的影響(換句話說就是很慢)。
但如果你要修改的屬性真的需要重繪畫面(例如表格的 colspan 屬性等等)時例外。
要修改元素的 CSS 樣式,可以存取 style 物件。
oneElement.style.paddingTop = '2rem'
要取得元素的 CSS 值,可以像上面一樣透過 style 物件,
也可以透過 window.getComputedStyle() 取得實際的值。
也可以透過 window.getComputedStyle() 取得實際的值。
window.getComputedStyle(oneElement).getPropertyValue('padding-top')
【三、修改 DOM】
// 在 element1 裡插入一個 element2
element1.appendChild(element2)
// 在 element1 裡的 element3 之前插入一個 element2
element1.insertBefore(element2, element3)
世界上有 insertBefore() 卻沒有 insertAfter(),所以必須繞個圈。
// 在 element1 裡的 element3 「之後」插入一個 element2
element1.insertBefore(element2, element3.nextSibling)
// 不能寫成:
// element1.insertAfter(element2, element3)
// 複製 DOM
const newElement = oneElement.cloneNode()element1.appendChild(newElement)
// 建立新的 DOM
const newElement = document.createElement('div')
const newTextNode = document.createTextNode('hello world')
// 移除 DOM,需要參照到親元素
parentElement.removeChild(element1)
// 自己移除自己
element1.parentNode.removeChild(element1)
要修改元素的內容,傳統的做法可以用 innerHTML:
oneElement.innerHTML = '<div>
<h1>hello world</h1>
</div>'
更好的做法是使用 DocumentFragment:
const text = document.createTextNode('continue reading...')
const hr = document.createElement('hr')
const fragment = document.createDocumentFragment()
fragment.appendChild(text)
fragment.appendChild(hr)
oneElement.appendChild(fragment)
【四、監聽事件】
JavaScript 最重要的就是監聽(listen)各種事件來觸發程式碼。
我們使用 addEventListener 來監聽事件處理。
oneElement.addEventListener('click', function (event) {
// do something...
})
同時監聽許多元素時,透過 event.target 來取得是哪個元素觸發的。
Array.from(allElements).forEach(element => {
element.addEventListener('change', function (event) {
console.log(event.target.value)
})
})
只想讓事件觸發一次(jQuery 的 once):
oneElement.addEventListener('change', function listener(event) {
console.log(event.type + ' got triggered on ' + this)
this.removeEventListener('change', listener)
})
【五、動畫】
以前習慣用 window.setTimeout() 來做動畫,
現在我們有更好更快的 window.requestAnimationFrame() 了。
const start = window.performance.now()
const duration = 2000
window.requestAnimationFrame(function fadeIn (now) {
const progress = now - start
oneElement.style.opacity = progress / duration
if (progress < duration) {
window.requestAnimationFrame(fadeIn)
}
}
【六、包裝】
最後我們可以把這些方式全部包在一個 function 裡。
就像 jQuery 一樣,還可以鍊式呼叫(chainable)
(例如: $('foo').css({color: 'red'}).on('click', () => {}) )
const $ = function $(selector, context = document) {
const elements = Array.from(context.querySelectorAll(selector))
return {
elements, html (newHtml) {
this.elements.forEach(element => {element.innerHTML = newHtml
})
return this
}, css (newCss) {
this.elements.forEach(element => {Object.assign(element.style, newCss)
})
return this
},on (event, handler, options) {
this.elements.forEach(element => {
element.addEventListener(event, handler, options)
})
return this
} // etc.
}}
或者用 ES6 的 Class 來包裝:
class DOM {
constructor(selector) {
const elements = document.querySelectorAll(selector)
this.length = elements.length
Object.assign(this, elements)
}
each(callback) {
for (let el of Array.from(this)) {
callback.call(el)
}
return this
}addClass(className) {
return this.each(function () {
this.classList.add(className)
})
}
removeClass(className) {
return this.each(function () {
this.classList.remove(className)
})
}
hasClass(className) {
return this[0].classList.contains(className)
}
on(event, callback) {
return this.each(function () {
this.addEventListener(event, callback, false)
})
}
// etc.
}--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.224.11.65
※ 文章代碼(AID): #1OvtCl7o (Ajax)
※ 文章網址: https://www.ptt.cc/bbs/Ajax/M.1491563311.A.1F2.html
※ 編輯: jmlntw (36.224.11.65), 04/07/2017 19:14:00
--
※ jmlntw:轉錄至看板 Web_Design 04/07 19:15
※ 編輯: jmlntw (36.224.11.65), 04/07/2017 19:30:14推 : 尸口巾1F 04/07 19:31
推 : 上色好認真…2F 04/07 23:40
→ : 還有你最後都包成 $ 了,那直接引用 jQuery 不就好了?
→ : 還幫你處理了兼容問題
→ : 還有你最後都包成 $ 了,那直接引用 jQuery 不就好了?
→ : 還幫你處理了兼容問題
You Might Not Need jQuery
Examples of how to do common event, element, ajax and utility operations with plain javascript. ...
Examples of how to do common event, element, ajax and utility operations with plain javascript. ...
GitHub - oneuijs/You-Dont-Need-jQuery: Examples of how to do query, style, dom, ajax, event etc like jQuery with plain javascript.
You-Dont-Need-jQuery - Examples of how to do query, style, dom, ajax, event etc like jQuery with plain javascript. ...
You-Dont-Need-jQuery - Examples of how to do query, style, dom, ajax, event etc like jQuery with plain javascript. ...
推 : 用心推 超讚8F 04/08 11:57
推 : 推 但是客戶死不轉換至IE11..繼續用$$$$$9F 04/08 12:49
推 : 看來也得等所有舊的瀏覽器都被淘汰了才能直接用......10F 04/08 13:35
推 : 推11F 04/08 13:50
→ : 在不用 ES6 的情況下,現在官方主流支援中的瀏覽器幾乎都12F 04/08 14:37
→ : 能使用。如果是特定環境(例如瀏覽器擴充功能或 Electron
→ : 等)那就更不是問題了。
→ : 當然和用 jQuery 比起來像是在重複造輪子,不過當作熟悉原
→ : 生 JS 的學習也不是不行。
→ : 能使用。如果是特定環境(例如瀏覽器擴充功能或 Electron
→ : 等)那就更不是問題了。
→ : 當然和用 jQuery 比起來像是在重複造輪子,不過當作熟悉原
→ : 生 JS 的學習也不是不行。
推 : 推一個~太用心了17F 04/08 14:45
推 : 好文推!!!!18F 04/08 15:34
推 : 好文推 原生的相當重要19F 04/09 11:02
推 : 客戶還在ie8,連es6都併進來用了20F 04/09 12:08
→ : 已眼神死,不在乎在幾十k的js...
→ : 已眼神死,不在乎在幾十k的js...
推 : 推用心22F 04/09 23:59
推 : 推用心, 請問有網頁版嗎23F 04/10 10:00
推 : 想到 IE,還是回頭乖乖用 jQuery (煙)24F 04/10 11:39
→ : 不過大大寫得不錯,建議可以弄個網頁,造福人群
→ : 不過大大寫得不錯,建議可以弄個網頁,造福人群
→ : PTT 有網頁版啊。26F 04/10 22:07
推 : 推27F 04/14 00:21
--
回列表(←)
分享