The introduction

The Web front end has evolved rapidly in the past few years, from the earliest jQuery to Angular, React, and Vue frameworks, and the way we manipulate the DOM has changed as well.

JQuery provides a number of quick ways to manipulate the DOM while maintaining compatibility. Frameworks such as React encapsulate the DOM, hide direct DOM manipulation, and provide methods such as ref. It may seem like we’re using the DOM less and less, but DOM manipulation is definitely an important part of Web development, and knowledge of it is a must.

The reason why I plan to comprehensively learn DOM operation is that I find my DOM operation is not elegant enough in my work. By comparing with the excellent writing method of the third party library, I find that I need to be improved.

The instance

Let’s take a look at a simple but common feature: for a pop-up, click on another blank space to hide it.

Click somewhere else to hide, the event needs to be bound to document, and the next step is to determine “somewhere else.” This logic is also very simple, determine whether the click object is a pop-up box, if not, belong to “other places”, so the realization of the previous judgment will also achieve this function.

The event is bound to the Document, so we get the clicked object via event.target and decide. Because event.target can be a DOM element at all levels of the pop-up content, it is necessary to determine whether the Event. Target itself is the outermost DOM element of the pop-up in the parent element chain.

// Document event binding is best done after the popup appears and also after the popup hides
document.addEventListener('click'.(event) = > {
    const popWrapper = ... // The outermost DOM of the pop-up box
    const target = event.target
    constisClickAway = target ! == popWrapper && ! isDescendant(popWrapper, target)if (isClickAway) {
        ... // close pop}})const isDescendant = function(parent, child) {
    let node = child.parentNode;
    while (node) {
        if (node === parent) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}
Copy the code

The above method can fulfill the requirements, and the code looks clean. I have done this before, but there are actually more elegant implementations, as follows:

// Document event binding is best done after the popup appears and also after the popup hides
document.addEventListener('click'.(event) = > {
    const popWrapper = ... // The outermost DOM of the pop-up box
    const isClickAway = popWrapper.contains(event.target)
    if (isClickAway) {
        ... // close pop}})Copy the code

DOM APIs provide the Contains method to determine directly, and there are no major compatibility issues (incompatibility can be solved by polyfill). You can see from the first implementation that the DOM APIs provide a direct method, so you don’t have to write code again.

So mastering the DOM APIs can improve your coding efficiency and code cleanliness.

DOM APIs

Here are some HTML DOM APIs or related knowledge you should know. The examples in this article are taken from the HTML DOM. This is an excellent site, I suggest bookmarking.

classList

You can use it when adding or removing class names. Some methods of IE are incompatible.

ele.classList.add('class-name')

Not supported in IE 11
ele.classList.add('another'.'class'.'name')

ele.classList.remove('class-name')

Not supported in IE 11
ele.classList.remove('another'.'class'.'name')

ele.classList.toggle('class-name')

ele.classList.contains('class-name')
Copy the code

The classnames library is typically used in real projects to handle managing classnames, but you still need to know about classlists.

querySelector, querySelectorAll

Query DOM elements through CSS selectors

// Returns the first element matched
document.querySelector('.demo')

// Return all matched elements
document.querySelectorAll('.demo')
Copy the code

This method should be very common and practical method nowadays.

matches

Determines whether the DOM element matches the provided CSS selector

const matches = function(ele, selector) {
    return (
        ele.matches || 
        ele.matchesSelector || 
        ele.msMatchesSelector || 
        ele.mozMatchesSelector || 
        ele.webkitMatchesSelector || 
        ele.oMatchesSelector
    ).call(ele, selector);
}
Copy the code

This method is more extensive than the Contains method of classList.

Determines whether a DOM element is a child of another element

Using the DOM APIs directly, this method also returns true for parent and child

const isDescendant = parent.contains(child)
Copy the code

Walk through to see the parent element

// Check if `child` is a descendant of `parent`
const isDescendant = function(parent, child) {
    let node = child.parentNode;
    while (node) {
        if (node === parent) {
            return true;
        }

        // Traverse up to the parent
        node = node.parentNode;
    }

    // Go up until the root but couldn't find the `parent`
    return false;
};
Copy the code

Get the CSS style

const styles = window.getComputedStyle(ele, null);

const bgColor = styles.backgroundColor;

const bgColor = styles.getPropertyValue('background-color');
Copy the code

Setting CSS Styles

Via the style attribute

ele.style.backgroundColor = 'red';
ele.style['backgroundColor'] = 'red';
ele.style['background-color'] = 'red';
Copy the code

Through cssText

el.style.cssText += 'background-color: red; color: white';
Copy the code

To remove CSS styles that cannot be removed by delete, use removeProperty

ele.style.removeProperty('background-color');

// This does not work
ele.style.removeProperty('backgroundColor');
Copy the code

Width and height of DOM elements

// Get styles const styles = window.getcomputedStyle (ele); Const height = ele.clientheight - parseFloat(styles.paddingTop) - parseFloat(styles.paddingBottom); const width = ele.clientWidth - parseFloat(styles.paddingLeft) - parseFloat(styles.paddingRight); Const clientHeight = ele.clientheight; const clientWidth = ele.clientWidth; // Width and height include inside margin and border const offsetHeight = ele.offsetheight; const offsetWidth = ele.offsetWidth; Const heightWithMargin = ele.offsetheight + parseFloat(styles.margintop) + // Width and height include highlighted and border const heightWithMargin = ele.offsetheight + parseFloat(styles.margintop) + parseFloat(styles.marginBottom); const widthWithMargin = ele.offsetWidth + parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);Copy the code

This is just a review of what the width property is getting at.

DOM adjacent insert element operation

Insert element after, insert ele after refEle

refEle.parentNode.insertBefore(ele, refEle.nextSibling);

// Or
refEle.insertAdjacentElement('afterend', ele);
Copy the code

Insert element in front, insert ele in front of refEle

refEle.parentNode.insertBefore(ele, refEle);

// Or
refEle.insertAdjacentElement('beforebegin', ele);
Copy the code

Preview uploaded images

Introduce two apis: url.createObjecturl () and FileReader’s readAsDataURL(). This can be used to preview images while uploading

<input type="file" id="fileInput" />
<img id="preview" />
Copy the code
const fileEle = document.getElementById('fileInput');
const previewEle = document.getElementById('preview');
Copy the code

Using URL. CreateObjectURL ()

fileEle.addEventListener('change'.function(e) {
    // Get the selected file
    const file = e.target.files[0];

    // Create a new URL that references to the file
    const url = URL.createObjectURL(file);

    // Set the source for preview element
    previewEle.src = url;
})
Copy the code

Use FileReader ‘s readAsDataURL ()

fileEle.addEventListener('change', function(e) {
    // Get the selected file
    const file = e.target.files[0];

    const reader = new FileReader();
    reader.addEventListener('load', function() {
        // Set the source for preview element
        previewEle.src = reader.result;
    });

    reader.readAsDataURL(file);
})
Copy the code

Select the literal content of the DOM element

Selection and range knowledge can be used for further study

const selectText = function(ele) {
    const selection = window.getSelection();
    const range = document.createRange();
    range.selectNodeContents(node);
    selection.removeAllRanges();
    selection.addRange(range);
}
Copy the code

Active trigger event

Trigger events. Some special events can be called directly because they are in the method properties of the element.

// For text box and textarea
inputEle.focus();
inputEle.blur();

// For form element
formEle.reset();
formEle.submit();

// For any element
ele.click();
Copy the code

Trigger native event

const trigger = function(ele, eventName) {
    const e = document.createEvent('HTMLEvents');
    e.initEvent(eventName, true.false);
    ele.dispatchEvent(e);
};

trigger(ele, 'mousedown')
Copy the code

Triggers a custom event

const e = document.createEvent('CustomEvent');
e.initCustomEvent('hello'.true.true, { message: 'Hello World' });

// Trigger the event
ele.dispatchEvent(e);
Copy the code

Scroll to an element position

ele.scrollIntoView();

Ie and Safari do not support the smooth parameter
ele.scrollIntoView({ behavior: 'smooth' });
Copy the code

conclusion

Every Web developer should have a grasp of HTML DOM APIs. The HTML DOM is a site that showcases many apis and provides excellent examples, from which most of the examples in this article are taken.

In addition to the simple apis, there are other apis that require further study, such as drag correlation, text selection and range, DOMParser, window.URL, and so on.