最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 封装自己的DOM

    正文概述 掘金(never_abandon)   2021-01-10   500

    封装思路

    因为在使用DOM的原生方法的时候总是感觉有很多代码特别的长不是很方便,例如addEventListener,我就想能不能把这些方法进行进一步的封装,形成一个自己可以使用的代码,这样比较的快捷及简便,在封装的过程中,还通过msdn进行了查询一些DOM的操作,找到了一些小窍门,大家可以看我具体实现中的实现方法,希望你可以在该篇文章中有所收获

    具体实现

    首先要先在window中封装一个对象.

    window.dom = {
      
    }
    

    初步封装创建的DOM方法

      // 传入参数为string
      create(string) {
        // 创建一个template包容器,这里面可以写任何的代码,但是div不可以写,所以使用的是template
        const container = document.createElement("template");
        // 将div的HTML设置为传入的string
        container.innerHTML = string;
        // 返回div的第一个子元素
        return container.children[0];
      }
    

    在封装该方法的时候,一开始想使用创建包围代码的容器是div,但是发现div在包含时有些属性不支持显示,比如说td等,然后就在网上进行了资料查询,发现在template内可以包含大部分的元素.
    在节点的后面插入节点的方法

      after(node,node2) {
        // 找到node的爸爸的节点调用,将node2插到node下一个节点的前面
        node.parentNode.insertBefore(node2, node.nextSibling);
      },
    

    由于原生的DOM没有提供在节点后面插入节点的方法,只提供了insertBefore,所以我们可以通过该方法来插入到节点的后面
    在节点的前面插入节点的方法

      before(node,node2) {
        // 由于自带的insertBefore方法,可以直接将node2插入到node的前面
        node.parentNode.insertBefore(node2, node);
      },
    

    给父节点新增一个子节点

      append (parent, child) {
        parent.appendChild(node)
      }
    

    给子节点增加一个父节点

      wrap(node, parent) {
        // 把parent放在node的前面
        dom.before(node, parent)
        // 把node放在parent的里面
        dom.append(parent, node)
      }
    

    这里用到了我们前面封装过得before和append,通过组合完成给子节点增加父节点的方法
    删除单个节点的方式

      // 删除节点
      remove(node) {
        // 找到该节点的父节点,然后删除父节点的子节点
        node.parentNode.removeChild(node);
        // 返回删除的节点以便引用
        return node
      },
    

    在这里我们没有直接使用remove的方法,而是调用了很多浏览器都支持的,找到父节点,再删除子节点的方法
    删除所有节点的方式

      // 删除所有的子节点
        empty(node) {
            // 直接删除
        // node.innerHTML = '';
        // 找到所有的子节点
        // const childNodes = node.childNodes;
        // 以上的简写
        const {childNodes} = node;
        // 保存删除的数组
        const array = []
        /* 遍历所有子节点 !!!这里的childNodes的长度是实时变化的所以这种方式有弊端
        for(let i=0;i<childNodes.length;i++) {
          // 将所有找到的节点移除
          dom.remove(childNodes[i]);
          // 将删除了的节点保存到数组中,返回以供使用
          array.push(childNodes[i])
        }
        return array*/
        // 设置x为node的第一个子节点
        let x = node.firstChild
        while(x){
          // remove后会有返回值,所以可以将删除的push到数组中
          array.push(dom.remove(node.firstChild))
          // 然后让x等于下一个子节点,直到没有子节点
          x = node.firstChild
        }
        // 返回数组
        return array
      }
    

    在删除节点的方法中我想到了三种方法:
    1.直接将节点的所有HTML都设置为空,这样很直接的删除了所有节点
    2.找到所有的子节点,然后同时遍历子节点然后一个个remove删除掉,但是当实践的时候发现当remove后再进行遍历,所有子节点的长度会实时发生变化,所以这个方法是不合理的.
    3.由于第二种方法的子节点长度会实时变化,就想到了另外一种的遍历方式,设置一个变量,然后调用while循环,只要有子节点,就将这个第一个子节点删除,然后将删除后的节点中的第一个节点设置为这个变量,再进行循环,如以上代码所示
    设置节点属性

      attr(node, name, value) { // 重载(根据参数个数写不同的代码)
        // 判断实参的长度,如果实参长度为3就设置 
        if(arguments.length === 3) {
          node.setAttribute(name, value)
        }else if(arguments.length === 2){
        // 判断实参的长度,如果实参长度为2就读取
          return node.getAttribute(name)
        }
      }
    

    这是一个用来设置节点的方法,在考虑的时候我们有考虑到传入实参的个数,同时考虑到用户实际想获取还是想设置的同时调用不同的方法
    设置节点的内容 会同时改掉该节点里面含有的子节点

      text(node, string) { // 适配两种浏览器
        if(arguments.length === 2){
          // 判断如果有则用,没有就用另一种
          if('innerText' in node) {
            node.innerText = string //该方法是IE的比较通用
          }else {
            node.textContent = string // 该方法火狐谷歌等用的比较多
          }
        }else if(arguments.length ===1) {
          if('innerText' in node) {
            return node.innerText //该方法是IE的比较通用
          }else {
            return node.textContent// 该方法火狐谷歌等用的比较多
          }
        }
      }
    

    此为设置节点内容,同样用到了重载的方法,判断用户是想读取还是想设置,并且根据个别浏览器的支持采用了不同的DOM操作以便进行适配
    改变内容的HTML

      html(node, string) {
        if(arguments.length === 2){
          // 判断如果有则用,没有就用另一种
          node.innerHTML = string
        }else if(arguments.length ===1) {
          return node.innerHTML
        }
      },
    

    修改节点的style属性

      style(node, name, value) {
        // 若长度为3 是直接设置style样式
        if(arguments.length === 3) {
          node.style[name] = value
          // 当长度为2时还需要判断第二个值类型为对象(设置),还是字符串(读取)
        }else if(arguments.length === 2) {
          if(typeof name === 'string') {
            return node.style[name]
          }else if(name instanceof Object) {
            // 读取到所有的key
            const object = name
            for(let key in object) {
              // 将每个的key设置为对象的key值
              node.style[key] = object[key]
            }
          }
        }
      },
    

    当实参为3个时直接设置即可,但是当2个实参是要进行判断,第二个实参是字符串的话就是获取,但是若为对象,则应进行遍历然后设置对应的name值
    查看移除设置class

      class: {
        // 添加
        add (node, className) {
          node.classList.add(className)
        },
        // 移除
        remove (node, className) {
          node.classList.remove(className)
        },
        // 查看有无
        has (node, className) {
          return node.classList.contains(className)
        }
      },
    

    在has方法中返回的为true or false
    添加和移除监听事件

      // 添加监听事件
      on(node, eventName, fn) {
        node.addEventListener(eventName, fn)
      },
      // 移除监听事件
      off(node, eventName, fn) {
        node.removeEventListener(eventName, fn)
      },
    

    运用添加和删除监听的方法,传入函数
    给选择器查对应的节点

      find(selector, scope) {
        // 返回的是一个数组
        // 若存在一个返回就用返回内 如果没有就在文档中找
        return (scope || document).querySelectorAll(selector)
      }
    

    要判断是在全局当中选择,还是有给指定范围,通过||操作符若有范围则直接取范围,若没有则在全局中选择
    找节点的父亲

      parent(node) {
        return node.parentNode
      }
    

    找节点的儿子

      children(node) {
        return node.children
      }
    

    用于获取兄弟姐妹,除了自己

      siblings(node) {
        // 将生成的伪数组转换成数组,再filter过滤
        return Array.from(node.parentNode.children).filter(x => {
          return x !== node
        })
      }
    

    在之前的文章中有提到过filter函数,可以用来过滤符合与否我们的需求,但是必须为数组才能调用,我们获取到该节点的父亲的子节点后,然后判断只要不是当前节点就返回到一个数组中.
    找到下一个节点

      next(node) {
        let x = node.nextSibling
        // 判断是否下一个是文本
        while(x && x.nodeType === 3) {
          x = x.nextSibling
          // 直到x是节点退出循环
        }
        return x
      }
    

    在这个找节点的方法中,我们想找到的是节点,但有时会返回一些空格的文本节点,不符合我们的要求,通过msdn中我查到node.nextSibling有返回值,若返回值为3时则为文本节点的方法,然后进行循环判断,只要不是文本节点则退出循环
    找到上一个节点

      prev(node) {
        let x = node.previousSibling
        // 判断是否下一个是文本
        while(x && x.nodeType === 3) {
          x = x.previousSibling
          // 直到x是节点退出循环
        }
        return x
      }
    

    和找到下一个节点有共同的方法,同样需要进行循环判断 遍历所有节点

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

    对所有节点进行遍历的同时,传入第二个函数,将函数的指向设置为空即可,可以同时设置节点的style
    找到自己排第几

      index(node) {
        // 获取所有孩子,判断什么时候是自己 然后停止,返回
        const list = dom.children(node.parentNode);
        let i
        for(i =0;i<list.length;i++) {
          if(list[i] === node) {
            break
          }
        }
        return i
      }
    

    用children方法找到节点的父节点的所有孩子,然后进行遍历,若和当前节点相同则停止,返回i,即可找到自己是第几.

    总结

    这是最近在学习js中学到了很重要的一个思路,要学会使用循环,大家可以看到上面有很多地方都运用到了循环的方法,同时还要学会对文档的查询,这是一种找到问题,解决问题的关键所在,今天的文章就分享到这里了,谢谢阅读.
    记得持续学习,不断跟进!加油!


    起源地 » 封装自己的DOM

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元