Skip to content

Latest commit

 

History

History
146 lines (115 loc) · 6.56 KB

understand-the-dom.md

File metadata and controls

146 lines (115 loc) · 6.56 KB

Item 75: Understand the DOM Hierarchy

Things to Remember

  • 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, and EventTarget, as well as those between Event and MouseEvent.
  • Either use a specific enough type for DOM elements and Events in your code or give TypeScript the context to infer it.

Code Samples

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'

💻 playground


function handleDrag(eDown: Event) {
  const targetEl = eDown.currentTarget;
  targetEl.classList.add('dragging');
  // ~~~~~           'targetEl' is possibly 'null'
  //       ~~~~~~~~~ Property 'classList' does not exist on type 'EventTarget'
  // ...
}

💻 playground


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

💻 playground


const div = document.getElementById('my-div');
//    ^? const div: HTMLElement | null

💻 playground


document.getElementById('my-div') as HTMLDivElement;

💻 playground


const div = document.getElementById('my-div');
if (div instanceof HTMLDivElement) {
  console.log(div);
  //          ^? const div: HTMLDivElement
}

💻 playground


const div = document.getElementById('my-div')!;
//    ^? const div: HTMLElement

💻 playground


function handleDrag(eDown: Event) {
  // ...
  const dragStart = [
     eDown.clientX, eDown.clientY
     //    ~~~~~~~        ~~~~~~~ Property '...' does not exist on 'Event'
  ];
  // ...
}

💻 playground


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);
}

💻 playground