作者jmlntw (吉米林)
看板Web_Design
标题Fw: [心得] 都2017年了 学学用原生JS来操作DOM吧
时间Fri Apr 7 19:15:24 2017
※ [本文转录自 Ajax 看板 #1OvtCl7o ]
作者: jmlntw (吉米林) 看板: 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/
【一、查询和取得 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)
// elementsOldw 会拿到 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(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
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Ajax/M.1491563311.A.1F2.html
※ 编辑: jmlntw (36.224.11.65), 04/07/2017 19:14:00
※ 发信站: 批踢踢实业坊(ptt.cc)
※ 转录者: jmlntw (36.224.11.65), 04/07/2017 19:15:24
※ 编辑: jmlntw (36.224.11.65), 04/07/2017 19:30:53
1F:推 MangoTW: 这不推不行啊! 04/08 01:15
2F:→ a42006310: 超赞 04/08 11:57
3F:推 william45682: 推 04/08 19:01
4F:推 tregfd654321: 推 04/09 01:36
5F:推 w2003x3: 推 04/09 02:13
6F:推 daniel54088: 推! 04/09 17:14
7F:推 TFreeman: 推! 04/10 06:53
8F:推 hunter77613: 赞赞! 04/11 15:53
9F:推 gokou542: 一些用法都好怀念XD 04/12 20:19
10F:推 BluePaint: 赞 04/14 06:40
11F:推 jye0807: 推! 05/11 23:18