一文带你深入理解JavaScript DOM操作与事件(全英)

92 阅读9分钟

不是标题党, 如果可以把下面的链接指向的资源都理解透彻, 当然可以深入理解DOM

完全新手的话, 可以先看看我写的key concept后, 再看Odin的文章之后慢慢深入

Resource

推荐The Odin Project的这一章节, 前面带我们简单入门了基本操作, 之后在Assignment给出了很好的Event的补充资源, 在附加资源中更是全面的覆盖了DOM知识点

the odin project知识点检验

the odin project 附加资源: 内含一些适合入门加进阶的内容:

  1. w3school的快速入门
  2. Youtube的Crash Course(两小时左右, 我觉得讲的不错!)
  3. DigitalOcean的一系列tutorial
  4. etc.

以下仅供参考

Key Concept:

DOM

what is DOM?

The DOM (or Document Object Model) is a tree-like representation of the contents of a webpage

  • which like a tree
  • using JS manipulate it
  • every node in a tree is an object

DOM nodes examples:

  • document the omnipotent one

How to select a element(node) in the DOM?

  • let header = document.getElementById("balabala");
  • let header = document.getElementByClassName("balabala");
  • let header = document.getElementByTagName("balabala");
  • let header = document.querySelector("balabala");
    • which is mostly used while it can use any CSS selector
  • header = document.querySelectorAll("balabala");
    • which return a "NodeList" object

Element attributes:

HTML / CSS stuff:

  • header.className = "header"
  • header.id = "header"
  • header.textContent = "AWESOME";
  • header.innerText = "AWESOME";
  • header.innerHTML = "AWESOME";
  • header.style.backgroundColor = red;
    • which change CSS!

Manipulation:

  • header.parentNode
  • header.childNode
  • header.children
  • header.nextElementSibiling

how to create a element and update it to HTML?

Create:

  • let newheader = document.createElement("header");
  • let newheader = document.createTextNode("just some text"); Update:
  • header.append(newHeader);
  • header.appendChild(newHeader);
  • header.insertBefore(newHeader);

Event

three way to click a button

  • btn.onclick = somefunction
  • <button onclick="somefunction">this is a button </button>
Mostly used
  • btn.addEventListener('click', somefunction);
    • which is essential way to listen a event!
    • the somefunction should take in a Event object
    • event.target represent the origin element
    • event.type show the type(which is click for now) of the event etc.

Various Events

mouseover / mouseout

mouseenter / mouseleave

keyUp / keyDown

focus / blur

change / input / etc. ...

How Events Response? # Two main Event models

Event bubbling

  1. button
  2. div with the id container
  3. body
  4. html
  5. document

Event capturing

  1. document
  2. html
  3. body
  4. div with the id container
  5. button

DOM Level 2 Event flow

...


FULL NOTE

BELOW IS MY FULL NOTES WHICH IS KINDA REDUNDANT, SO JUST IGNORE IT.

Two main Event models

Event bubbling

  1. button
  2. div with the id container
  3. body
  4. html
  5. document

Event capturing

  1. document
  2. html
  3. body
  4. div with the id container
  5. button

DOM Level 2 Event flow

DOM level 2 events specify that event flow has three phases:

  • First, event capturing occurs, which provides the opportunity to intercept the event.
  • Then, the actual target receives the event.
  • Finally, event bubbling occurs, which allows a final response to the event. |500

Event object

The following table shows the most commonly used properties and methods of the event object:

Property / MethodDescription
bubblestrue if the event bubbles
cancelabletrue if the default behavior of the event can be canceled
currentTargetthe current element on which the event is firing
defaultPreventedreturn true if the preventDefault() has been called.
detailmore information about the event
eventPhase1 for capturing phase, 2 for target, 3 for bubbling
preventDefault()cancel the default behavior for the event. This method is only effective if the cancelable property is true
stopPropagation()cancel any further event capturing or bubbling. This method only can be used if the bubbles property is true.
targetthe target element of the event
typethe type of event that was fired

Note that the event object is only accessible inside the event handler. Once all the event handlers have been executed, the event object is automatically destroyed.

e.g.

1. preventDefault()

For example, when you click a link, the browser navigates you to the URL specified in the href attribute:

<a href="https://www.javascripttutorial.net/">JS Tutorial</a>`

However, you can prevent this behavior by using the preventDefault() method of the event object:

let link = document.querySelector('a');
link.addEventListener('click',function(event) {
	console.log('clicked');
	event.preventDefault();
	});

Note that the preventDefault() method does not stop the event from bubbling up the DOM. An event can be canceled when its cancelable property is true.

2. stopPropagation()

The stopPropagation() method immediately stops the flow of an event through the DOM tree. However, it does not stop the browser’s default behavior.

let btn = document.querySelector('#btn');

btn.addEventListener('click', function(event) {
    console.log('The button was clicked!');
    event.stopPropagation();
});

document.body.addEventListener('click',function(event) {
    console.log('The body was clicked!');
});
  • 默认情况下,addEventListener 是在 冒泡阶段 监听事件的,所以 "The body was clicked!" 不会在捕获阶段触发。
  • 如果你想在捕获阶段监听事件,可以设置 useCapture 参数为 true

Without the stopPropagation() method, you would see two messages on the Console window. However, the click event never reaches the body because the stopPropagation() was called on the click event handler of the button.

JavaScript Page Load Events

When you open a page, the following events occur in sequence:

  • DOMContentLoaded 
    • the browser fully loaded HTML and completed building the DOM tree.
    • However, it hasn’t loaded external resources like stylesheets and images.
    • In this event, you can start selecting DOM nodes or initialize the interface.
  • load 
    • the browser fully loaded the HTML and external resources like images and stylesheets.

When you leave the page, the following events fire in sequence:

  • beforeunload
    • fires before the page and resources are unloaded.
    • You can use this event to show a confirmation dialog to confirm if you want to leave the page.
    • By doing this, you can prevent data loss in case the user is filling out a form and accidentally clicks a link that navigates to another page.
  • unload 
    • fires when the page has completely unloaded.
    • You can use this event to send the analytic data or to clean up resources.

The following example illustrates how to handle the page load events

<!DOCTYPE html>
<html>
<head>
    <title>JS Page Load Events</title>
</head>

<body>
    <script>
        addEventListener('DOMContentLoaded', (event) => {
            console.log('The DOM is fully loaded.');
        });

        addEventListener('load', (event) => {
            console.log('The page is fully loaded.');
        });

        addEventListener('beforeunload', (event) => {
            // show the confirmation dialog
            event.preventDefault();
            // Google Chrome requires returnValue to be set.
            event.returnValue = '';
        });

        addEventListener('unload', (event) => {
            // send analytic data
        });
    </script>
</body>
</html>

JavaScript Mouse Events

mousedown, mouseup, and click events

When you click an element, there are no less than three mouse events fire in the following sequence:

  1. The mousedown fires when you press the mouse button on the element.
  2. The mouseup fires when you release the mouse button on the element.
  3. The click fires when one mousedown and one mouseup detected on the element. JavaScript mouse event - click event

If you press the mouse button on an element, move your mouse cursor off the element, and then release the mouse button. The only mousedown event fires on the element.

Likewise, if you press the mouse button, move the mouse over the element, and release the mouse button, the only mouseup event fires on the element.

In both cases, the click event never fires.

dblclick event

  1.  mousedown
  2.  mouseup
  3.  click
  4.  mousedown
  5.  mouseup
  6.  click
  7.  dblclick If you register both click and dblclick event handlers on the same element you will not know whether the user has clicked or double-clicked the element.

 mousemove

The mousemove event fires repeatedly whenever you move the mouse cursor around an element. This mousemove event fires many times per second as the mouse is moved around, even if it is just by one pixel.

This may lead to a performance issue if the event handler function is complex. To avoid the performance issue, it is a good practice to add mousemove event handler only when you need it and remove it as soon as it is no longer needed, like this:

element.onmousemove = mouseMoveEventHandler;
// ... //  later, no longer use 
element.onmousemove = null;

mouseover / mouseout

  • The mouseover fires when the mouse cursor is outside of the element and then moves inside the boundaries of the element.
  • The mouseout fires when the mouse cursor is over an element and then moves another element.

mouseenter / mouseleave

  • The mouseenter fires when the mouse cursor is outside of an element and then moves inside the boundaries of the element.
  • The mouseleave fires when the mouse cursor is over an element and then moves to the outside of the element’s boundaries. Both mouseenter and mouseleave does not bubble and ==does not fire== when the mouse cursor moves over descendant elements.

key difference between mouseover and mouseenter etc.

1. Bubbling Behavior:
  • mouseover / mouseout: These events bubble up, meaning they trigger not only when the mouse moves over or out of the element but also when it interacts with any of the child elements. If you hover over a child element inside the parent, mouseover and mouseout will fire for both the parent and the child.
  • mouseenter / mouseleave: These events do not bubble. They only trigger when the mouse enters or leaves the element itself, ignoring any child elements. This makes them more efficient when you don’t want events firing multiple times when hovering over nested elements.
2. Firing Frequency:
  • mouseover / mouseout: These events can fire multiple times if you move your mouse across different parts of an element or its children.
  • mouseenter / mouseleave: These events will fire only once when entering or leaving the element, making them more stable for detecting entry/exit without worrying about child elements.
When to Use:
  • mouseover / mouseout: Use when you want the event to detect interactions with both parent and child elements (and you're fine with bubbling).
  • mouseenter / mouseleave: Use when you only care about the parent element and want to avoid the event firing for child elements.

Detecting mouse buttons

  • 0: the main mouse button is pressed, usually the left button.
  • 1: the auxiliary button is pressed, usually the middle button or the wheel button.
  • 2: the secondary button is pressed, usually the right button.
  • 3: the fourth button is pressed, usually the Browser Back button.
  • 4: the fifth button is pressed, usually the Browser Forward button.
<!DOCTYPE html>
<html>
<head>
    <title>JS Mouse Events - Button Demo</title>
</head>
<body>
    <button id="btn">Click me with any mouse button: left, right, middle, ...</button>
    <p id="message"></p>
    <script>
        let btn = document.querySelector('#btn');

        // disable context menu when right-mouse clicked
        btn.addEventListener('contextmenu', (e) => {
            e.preventDefault();
        });

        // show the mouse event message
        btn.addEventListener('mouseup', (e) => {
            let msg = document.querySelector('#message');
            switch (e.button) {
                case 0:
                    msg.textContent = 'Left mouse button clicked.';
                    break;
                case 1:
                    msg.textContent = 'Middle mouse button clicked.';
                    break;
                case 2:
                    msg.textContent = 'Right mouse button clicked.';
                    break;
                default:
                    msg.textContent = `Unknown mouse button code: ${event.button}`;
            }
        });
    </script>
</body>
</html>

Modifier keys (shift alt ctrl)

<!DOCTYPE html>
<html>
<head>
    <title>JS Modifier Keys Demo</title>
</head>
<body>
    <button id="btnKeys">Click me with Alt, Shift, Ctrl pressed</button>
    <p id="messageKeys"></p>

    <script>
        let btnKeys = document.querySelector('#btnKeys');

        btnKeys.addEventListener('click', (e) => {
            let keys = [];

            if (e.shiftKey) keys.push('shift');
            if (e.ctrlKey) keys.push('ctrl');
            if (e.altKey) keys.push('alt');
            if (e.metaKey) keys.push('meta');

            let msg = document.querySelector('#messageKeys');
            msg.textContent = `Keys: ${keys.join('+')}`;
        });
    </script>
</body>
</html>

Getting Screen Coordinates

The screenX and screenY properties of the event passed to the mouse event handler return the screen coordinates of the location of the mouse in relation to the entire screen. JavaScript mouse event -screenX screenY On the other hand, the clientX and clientY properties provide the horizontal and vertical coordinates within the application’s client area at which the mouse event occurred: See the following demo:

<!DOCTYPE html>
<html>
<head>
    <title>JS Mouse Location Demo</title>
    <style>
        #track {
            background-color: goldenrod;
            height: 200px;
            width: 400px;
        }
    </style>
</head>
<body>
    <p>Move your mouse to see its location.</p>
    <div id="track"></div>
    <p id="log"></p>

    <script>
        let track = document.querySelector('#track');
        track.addEventListener('mousemove', (e) => {
            let log = document.querySelector('#log');
            log.innerText = `
            Screen X/Y: (${e.screenX}, ${e.screenY})
            Client X/Y: (${e.clientX}, ${e.clientY})`
        });
    </script>
</body>
</html>

side notes: The wheel event is triggered when the mouse wheel is used.

JavaScript Keyboard Events

There are three main keyboard events:

  • keydown – fires when you press a key on the keyboard and fires repeatedly while you’re holding down the key.
  • keyup – fires when you release a key on the keyboard.
  • keypress – fires when you press a character keyboard like a,b, or c, not the left arrow key, home, or end keyboard, … The keypress also fires repeatedly while you hold down the key on the keyboard.

The keyboard events typically fire on the text box, though all elements support them. When you press a character key once on the keyboard, three keyboard events are fired in the following order:

  1. keydown
  2. keypress
  3. keyup

Both keydown and keypress events are fired before any change is made to the text box, whereas the keyup event fires after the changes have been made to the text box.

If you hold down a character key, the keydown and keypress are fired repeatedly until you release the key.

When you press a non-character key, the keydown event is fired first followed by the keyup event. If you hold down the non-character key, the keydown is fired repeatedly until you release the key.

The keyboard event properties

The keyboard event has two important properties: key and code. The key property returns the character that has been pressed whereas the code property returns the physical key code.

For example, if you press the z character key, the event.key returns z and event.code returns KeyZ.

JavaScript Event Delegation

FROM

<ul id="menu">
    <li><a id="home">home</a></li>
    <li><a id="dashboard">Dashboard</a></li>
    <li><a id="report">report</a></li>
</ul>
let home = document.querySelector('#home');
home.addEventListener('click',(event) => {
    console.log('Home menu item was clicked');
});

let dashboard = document.querySelector('#dashboard');
dashboard.addEventListener('click',(event) => {
    console.log('Dashboard menu item was clicked');
});

let report = document.querySelector('#report');
report.addEventListener('click',(event) => {
    console.log('Report menu item was clicked');
});

TO

let menu = document.querySelector('#menu');

menu.addEventListener('click', (event) => {
    let target = event.target;

    switch(target.id) {
        case 'home':
            console.log('Home menu item was clicked');
            break;
        case 'dashboard':
            console.log('Dashboard menu item was clicked');
            break;
        case 'report':
            console.log('Report menu item was clicked');
            break;
    }
});

JavaScript event delegation benefits

When it is possible, you can have a single event handler on the document that will handle all the events of a particular type. By doing this, you gain the following benefits:

  • Less memory usage, better performance.
  • Less time is required to set up event handlers on the page.
  • The document object is available immediately. As long as the element is rendered, it can start functioning correctly without delay. You don’t need to wait for the DOMContentLoaded or load events.

JavaScript dispatchEvent

Events can be generated from code.

  • First, create a new Event object using Event constructor.
  • Then, trigger the event using element.dispatchEvent() method.

Event constructor

To create a new event, you use the Event constructor like this:

let event = new Event(type, [,options]);

The Event constructor accepts two parameters:

  • type is a string that specifies the event type such as 'click'.
  • options is an object with two optional properties:
    • bubbles: is a boolean value that determines if the event bubbles or not. If it is true then the event is bubbled and vice versa.
    • cancelable: is also a boolean value that specifies whether the event is cancelable when it is true. By default, the options object is:
    { bubbles: false, cancelable: false}
    

For example, the following creates a new click event with the default options object:

let clickEvent = new Event('click');

dispatchEvent method

After creating an event, you can fire it on a target element using the dispatchEvent() method like this:

element.dispatchEvent(event);

For example, the following code shows how to create the click event and fire it on a button: HTML:

<button class="btn">Test</button>

JavaScript:

let btn = document.querySelector('.btn');
 btn.addEventListener('click', function () {
        alert('Mouse Clicked');
 });
let clickEvent = new Event('click');
btn.dispatchEvent(clickEvent);
  • Use event.isTrusted to examine whether the event is generated from code or user actions. It’s a good practice to use the specialized event constructor like MouseEvent instead of using the generic Event type because these constructors provide more information specific to the events. For example, the MouseEvent event has many other properties such as clientX and clientY 

JavaScript Custom Events

Creating JavaScript custom events

To create a custom event, you use the CustomEvent() constructor:

let event = new CustomEvent(eventType, options);

The CustomEvent() has two parameters:

  • The eventType is a string that represents the name of the event.
  • The options is an object has the detail property that contains any custom information about the event.

JavaScript custom event example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Custom Event</title>
</head>
<body>
    <div class="note">JS Custom Event</div>

    <script>
        function highlight(elem) {
            const bgColor = 'yellow';
            elem.style.backgroundColor = bgColor;

            // create the event
            let event = new CustomEvent('highlight', {
                detail: {
                    backgroundColor: bgColor
                }
            });

            // dispatch the event
            elem.dispatchEvent(event);
        }

        // Select the div element
        let div = document.querySelector('.note');

        // Add border style
        function addBorder(elem) {
            elem.style.border = "solid 1px red";
        }

        // Listen to the highlight event
        div.addEventListener('highlight', function (e) {
            addBorder(this);
            // examine the background
            console.log(e.detail);
        });

        // highlight div element
        highlight(div);
    </script>
</body>
</html>

How it works:

  • First, declare the highlight() function that highlights an element and triggers the highlight event.
  • Second, select the <div> element by using the [querySelector()](https://www.javascripttutorial.net/javascript-dom/javascript-queryselector/) method.
  • Third, listen to the highlight event. Inside the event listener, call the addBorder() function and show the detail property in the Console.
  • Finally, call the highlight() function that will trigger the highlight event.