The DOM API is really hard to use, each API stinks and has a long source code link: github.com/huyiling111… We provide a global window.dom object

// How to declare an object as a global object? // Mount the object to windowCopy the code

Next, “add, delete, change and check” are explained

First, add elements, add nodes

Create node dom.create(<div>hi</div>)

The DOM API requires document.createElement(‘div’), div. InnerText = ‘hi’ and we only need one sentence here, dom.create(

hi

)

 // Target effect
    // Type create("
      
hello
")
// Automatically create div and span // Write the string directly into InnerHTML // We use template because this tag can hold all tags, // div can't put a tag, whereas template can create(string) { const container = document.createElement("template") container.innerHTML = String.prototype.trim.call(string) // Remove extra Spaces return container.content.firstChild }, Copy the code

Two keys:

  1. The template tag is used as the Container because it can hold all the tags
  2. Trim () to remove extra whitespace because whitespace also counts as nodes and may interfere with our results

Add brother dom.after(node, node2)

Ideas: Since DOM only provides an Insertbefore operation, not an insertAfter operation, So we need to use a little bit of dark magic let’s say we have two nodes div1 — > div3 and we want to add div2 after div1, which is equivalent to inserting div2 before div3 so we have node.nextsibling to get the next node of the current node, InsertBefore again

after(node, newNode) {
        // The goal is to insert node2 after Node
        // But DOM only provides an insertBefore interface
        / / 1 - > 3
        // Inserting 2 after 1 is equivalent to inserting 2 before 3
        // So we switch to inserting node2 before the next node of node
        node.parentNode.insertBefore(newNode, node.nextSibling)
    },
Copy the code

Add brother dom.before(node, node2)

Find the father node, then insertBefore

before(node, newNode) {
        node.parentNode.insertBefore(newNode, node)
 },
Copy the code

Add child dom. Append (parent, child)

Find the parent node (appendChild)

append(parent, node) {
        parent.appendChild(node)
    },
Copy the code

New dad dom.wrap(<div></div>)

wrap(node, newParent) {
        // put Newparent in front of node
        // Add node append to newparent
        // Target: div1
        / / left -- -- -- -- > div2
        / / div1
        / / left -- -- -- -- > div3
        / / left -- -- -- -- > div2
  			Div3. AppendChild (div2)
        node.before.call(null, node, newParent)
        newParent.append(node)
    },
Copy the code

Usage:

const div3 = dom.create('<div id="parent"></div>')
dom.wrap(test, div3)
Copy the code

2. Delete a node

Remove node dom.remove(node)

RemoveChild = removeChild

 remove(node) {
        node.parentNode.removeChild(node)
        return node
    },
Copy the code

Delete descendant dom.empty(parent)

An easy way to think about it is to loop through Parent. ChildNodes and remove each time but there is a pit called childNodes. Length and the length changes in real time so here we use a while loop as long as the node has children, I just keep iterating

 // empty deletes all child nodes
    // pit: childNodes. Length The length changes each time
    empty(node) {
        // const {childNodes} = node is equivalent to const childNodes = node.childnodes
        const array = []
        let x = node.firstChild
        while (x) {
            array.push(dom.remove(node.firstChild))
            x = node.firstChild
        }
        return array
    },
Copy the code

Three, modify,

Read property dom.attr(node, ‘title’,?)

So we’re using overloading, so we’re doing different things when the function has different parameters when there’s only two parameters, we’re reading the property, we’re returning the property when there’s three parameters, we’re modifying the property and we’re using getter and setter design patterns because we can read and write the property at the same time

// Implement different functions depending on the number of arguments. This is called function overloading
    attr(node, name, value) {
        if (arguments.length === 3) {
            node.setAttribute(name, value)
        } else if (arguments.length === 2) {
            return node.getAttribute(name)
        }
    },
Copy the code

Method of use


      
test
// the title attribute of #test is hello world dom.attr(test, 'title'.'hello world') // Modify attributes const title = dom.attr(test, 'title') / / read the properties console.log(`title: ${title}`) Copy the code

Read and write text content dom.text(node,?)

InnerText = string if there is only one parameter, we return node.innerText. If there are two parameters, we write the innerText

text(node, string) {
        if (arguments.length === 2) {
            // Adapt to different browsers
            if ('innerText' in node) { //ie
                node.innerText = string
            } else { // firefox / chrome
                node.textContent = string
            }
        } else if (arguments.length === 1) {
            if ('innerText' in node) { //ie
                return node.innerText
            } else { // firefox / chrome
                return node.textContent
            }
        }

    },
Copy the code

Read and write HTML content dom.html(node,?)

 html(node, string) {
        if (arguments.length === 2) {
            / / modify
            node.innerHTML = string
        } else if (arguments.length === 1) {
            // Get the content
            return node.innerHTML
        }
    },
Copy the code

Modify dom.style(node, {color:’red’})

Node.style. color = ‘red’ Note that there are three cases where the style function will be used

  1. Dom.style (node, ‘color’, ‘red’) sets a property
  2. Dom.style (node, ‘color’) reads a property
  3. Dom.style (node, {‘color’: ‘red’, ‘border’: ‘1px solid black’}) pass an object and set multiple properties simultaneously

So we need to deal with different parameters, again using the adapter pattern first we need to determine the number of parameters,

  • Node.style. color = ‘red’
  • If it is 2
    • If the second argument is a string
      • Typeof XXXXX === ‘string’
typeof name === 'string'
Copy the code
- Then return node.style[name]Copy the code
  • If the second argument is an object
    • Instanceof: XXX instanceof Object === true
    • And then iterate over the object
    • Iterate over an object
for (let key in object) {
   ......                  
}
Copy the code

Implementation code:

/ / change style
    style(node, name, value) {
        if (arguments.length === 3) {
            // dom.style(div, 'color', 'red')
            node.style[name] = value
        } else if (arguments.length === 2) {
            if (typeof name === 'string') {
                //dom.style(test, 'border')
                // Get a CSS property
                return node.style[name]
            }

            if (name instanceof Object) {
                //dom.style(test, {border: '1px solid red', color: 'blue'})
                let object = name
                for (let key in object) {
                    // key : border / color
                    // node.style.border = ....
                    // node.style.color = ...
                    node.style[key] = object[key]
                }
            }
        }

    },
Copy the code

Add class dom.class.add(node, ‘blue’)

Node.classlist.add (class)

  class: {
        add(node, className) {
            node.classList.add(className)
        },
        remove(node, className) {
            node.classList.remove(className)
        },
        has(node, className) {
            return node.classList.contains(className)
        }
    },
Copy the code

Note: The contains method is used to find if an element’s classlist contains a class

Add event listener dom.on(node, ‘click’, fn)

 on(node, eventName, fn) {
        node.removeEventListener(eventName, fn)
    },
Copy the code

Remove event listener dom.off(node, ‘click’, fn)

 off(node, eventName, fn) {
        node.removeEventListener(eventName, fn)
    },
Copy the code

Four,

Get tags or tags dom.find(‘ selector ‘, scope)

Document. QuerySelector (‘selector’)

 // Get the element according to the selector
    find(selector, scope) {
        return (scope || document).querySelectorAll(selector)
    },
Copy the code

Get the parent element dom.parent(node)

parent(node) {
        return node.parentNode
},
Copy the code

Get the child element dom.children(node)

children(node) {
        return node.children
},
Copy the code

Gets the dom.siblings(node) element

Find the dad node and delete the element that’s me

 siblings(node) {
        Of all my parents' children, either I am my brother or my sister
        return Array.from(node.parentNode.children).filter(n= >n ! == node) },Copy the code

Next (node)

Note: while (x && x.nodetype! == 1) means if x exists and x is not an HTML element node, we go on to the next node until we find a node that is an HTML element node and the reason for this is that nodes contain HTML tags, text, comments, etc., so we need to do this to make sure we find an HTML tag

next(node) {
        let x = node.nextSibling
        while(x && x.nodeType ! = =1) {
           // x is not the desired HTML tag
            x = x.nextSibling
        }
        return x
    },
Copy the code

Get older dom. Previous (node)

 prev(node) {
        let x = node.previousSibling
        while(x && x.nodeType ! = =1) {
            // x is not the desired HTML tag
            x = x.previousSibling
        }
        return x

    }
Copy the code

Dom.each (Nodes, fn)

each(nodelist, fn) {
        for (let i = 0; i < nodelist.length; i++) {
            fn.call(null, nodelist[i])
        }
}
Copy the code

Dom. Index (node)

index(node) {
        let nodeList = dom.children(dom.parent(node))
        let i
        for (i = 0; i < nodeList.length; i++) {
            console.log(nodeList[i])
            if (nodeList[i] === node) {
                return i
            }
        }
       return -1 // Not found
    }
Copy the code