20. 封装原生Dom

343 阅读2分钟

原生的Dom操作api设计的过于反人性, 所以我想自己重新封装一下

20.1 增加节点

  1. 创建节点:

        create(string){
            const container = document.createElement("template")
            container.innerHTML = string.trim();
            return container.content.firstChild;
        }
    

    注意: 为什么使用template标签,而不是用常用的div标签呢? 因为div作为容器不能容纳td标签, 也就无法生成相应节点. 只有template能容纳所有标签,并且生成节点

  2. 添加节点:

        //增加右节点
    	after(node, node2){
            node.parentElement.insertBefore(node2, node.nextSibling)
        },
        //增加左节点
        before(node, node2){
            node.parentElement.insertBefore(node2, node)
        },
        //增加孩子节点
        append(parent, child){
            parent.appendChild(child)
        },
        //用父节点进行包裹
        wrap(node, parentNode){
            dom.before(node, parentNode)
            dom.append(parentNode, node)
        },
    

20.2 删除节点

  1. 删除当前节点:

        remove(node){
            return node.parentElement.removeChild(node)
        },
    
  2. 清空所有孩子节点:

        empty(parent) {
            const array = []
            while (parent.firstChild){
                array.push(parent.firstChild)
                parent.removeChild(parent.firstChild)
            }
            return array
        },
    

20.3 修改节点

  1. 修改节点属性

        attr(node, name, value){
            if(arguments.length === 3){
                node.setAttribute(name, value)
            }else if(arguments.length === 2){
                return node.getAttribute(name)
            }
        },
    

    注意:if(arguments.length === 3)这个if语句是为了实现函数重载. 3个参数就是修改属性, 2个参数就是查询属性

  2. 修改节点文字

    text(node, string){
            if(arguments.length === 2){
                if(`innerText` in node){
                    node.innerText = string
                }else{
                    node.textContent = string
                }
            }else if(arguments.length === 1){
                if(`innerText` in node){
                    return  node.innerText
                }else{
                    return node.textContent
                }
            }
        },
    

    注意:if("innerText" in node)这个if语句是为了实现兼容所有浏览器, innerText是微软设计的, textContent是W3C标准

  3. 修改html元素

        html(node, string){
          if(arguments.length === 2){
              node.innerHTML = string
          }else if(arguments.length === 1){
              return node.innerHTML
          }
        },
    
  4. 修改style

        style(node, name, value){
            if(arguments.length === 3){
                node.style[name] = value
            }else if(arguments.length === 2){
                if(typeof name === `string`){
                    return node.style[name]
                }else if(name instanceof Object){
                    const object = name
                    for(let key in object){
                        node.style[key] = object[key]
                    }
                }
            }
        },
    

    **注意:**这里的node.style[name]只能用中括号, 不能用.. 因为这里的name是一个变量, 而不是"name"字符串.

    ​ 这里的else if(name instanceof Object): 为了兼容一次性修改多个值, 所以传进来对象

  5. 修改属性class

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

20.4 查找节点

  1. 设定范围查找节点

        find(selector, scope){
            return (scope || document).querySelectorAll(selector)
        },
    
  2. 查找自己的父节点.

        parent(node){
            return node.parentNode
        },
    
  3. 查找自己的孩子节点.

        children(node){
            return node.children
        },
    
  4. 查找自己同辈节点.

        siblings(node){
            return Array.from(node.parentNode.children).filter(n=>n!==node)
        },
    
  5. 查找自己的弟弟, 哥哥.

        next(node){
            let x = node.nextSibling
            while(x && x.nodeType === 3){
                x = x.nextSibling
            }
            return x
        },
        previous(node){
            let x = node.previousSibling
            while(x && x.nodeType === 3){
                x = x.previousSibling
            }
            return x
        },
    

    注意:while(x && x.nodeType === 3)这个判断的原因: 因为要把类似于回车, 空格之类的文本元素过滤掉

  6. 查找索引:

        //重写each函数
    	each(nodeList, fn){
            for(let i=0;i<nodeList.length;i++){
                fn.call(null, nodeList[i])
            }
        },
        index(node){
            const list = dom.children(node.parentNode)
            let i
            for(i=0;i<list.length;i++){
                if(list[i] === node){
                    break
                }
            }
            return i
        }
    

20.5 事件的增加和删除

    on(node, eventName, fn){
        node.addEventListener(eventName, fn)
    },
    of(node, eventName, fn){
        node.removeEventListener(eventName, fn)
    },