Item 75: Understand the DOM Hierarchy
要点
- The DOM has a type hierarchy that you can usually ignore while writing JavaScript. But these types become more important in TypeScript. Understanding them will help you write TypeScript for the browser.
- Know the differences between
Node
,Element
,HTMLElement
, andEventTarget
, as well as those betweenEvent
andMouseEvent
. - Either use a specific enough type for DOM elements and Events in your code or give TypeScript the context to infer it.
正文
ts
function handleDrag(eDown: Event) {
const targetEl = eDown.currentTarget
targetEl.classList.add('dragging')
// ~~~~~ 'targetEl' is possibly 'null'
// ~~~~~~~~~ Property 'classList' does not exist on type 'EventTarget'
const dragStart = [
eDown.clientX,
eDown.clientY,
// ~~~~~~~ ~~~~~~~ Property '...' does not exist on 'Event'
]
const handleUp = (eUp: Event) => {
targetEl.classList.remove('dragging')
// ~~~~~ 'targetEl' is possibly 'null'
// ~~~~~~~~~ Property 'classList' does not exist on type 'EventTarget'
targetEl.removeEventListener('mouseup', handleUp)
// ~~~~~ 'targetEl' is possibly 'null'
const dragEnd = [
eUp.clientX,
eUp.clientY,
// ~~~~~~~ ~~~~~~~ Property '...' does not exist on 'Event'
]
console.log(
'dx, dy = ',
[0, 1].map((i) => dragEnd[i] - dragStart[i])
)
}
targetEl.addEventListener('mouseup', handleUp)
// ~~~~~ 'targetEl' is possibly 'null'
}
const surfaceEl = document.getElementById('surface')
surfaceEl.addEventListener('mousedown', handleDrag)
// ~~~~~~ 'surfaceEl' is possibly 'null'
ts
function handleDrag(eDown: Event) {
const targetEl = eDown.currentTarget
targetEl.classList.add('dragging')
// ~~~~~ 'targetEl' is possibly 'null'
// ~~~~~~~~~ Property 'classList' does not exist on type 'EventTarget'
// ...
}
ts
const p = document.getElementsByTagName('p')[0]
// ^? const p: HTMLParagraphElement
const button = document.createElement('button')
// ^? const button: HTMLButtonElement
const div = document.querySelector('div')
// ^? const div: HTMLDivElement | null
ts
const div = document.getElementById('my-div')
// ^? const div: HTMLElement | null
ts
document.getElementById('my-div') as HTMLDivElement
ts
const div = document.getElementById('my-div')
if (div instanceof HTMLDivElement) {
console.log(div)
// ^? const div: HTMLDivElement
}
ts
const div = document.getElementById('my-div')!
// ^? const div: HTMLElement
ts
function handleDrag(eDown: Event) {
// ...
const dragStart = [
eDown.clientX,
eDown.clientY,
// ~~~~~~~ ~~~~~~~ Property '...' does not exist on 'Event'
]
// ...
}
ts
function addDragHandler(el: HTMLElement) {
el.addEventListener('mousedown', (eDown) => {
const dragStart = [eDown.clientX, eDown.clientY]
const handleUp = (eUp: MouseEvent) => {
el.classList.remove('dragging')
el.removeEventListener('mouseup', handleUp)
const dragEnd = [eUp.clientX, eUp.clientY]
console.log(
'dx, dy = ',
[0, 1].map((i) => dragEnd[i] - dragStart[i])
)
}
el.addEventListener('mouseup', handleUp)
})
}
const surfaceEl = document.getElementById('surface')
if (surfaceEl) {
addDragHandler(surfaceEl)
}