封装一个DOM库

本文介绍了一个自定义的DOM库,包括对象风格的DOM操作,如创建、增加、删除、修改和查找节点的功能。提供了create、after、before、append、wrap等函数,以及attr、text、html、style、class等修改节点属性和样式的功能。同时,还实现了on和off函数用于事件监听,以及find、parent、children、siblings、next、previous等查找相关节点的方法。
摘要由CSDN通过智能技术生成

手写DOM

因为DOM原生API太难用了

源代码链接

对象风格

  • window.dom 是我们提供的全局对象

增加节点

  • dom.append(parent,child)  // 用于新增儿子节点
    
  • dom.wrap(`<div></div>`)   // 用于新增爸爸节点
    

dom.create 函数

dom.create(`<div><span>hi</span></div>`) // 用于创建节点
  • 一般我们创造节点的目的就是在别的节点中插入此节点,
  • 那么我要封装一个 以输入 html格式的 的 create函数
  • 能够在创造节点的同时在里面加一些其他节点
  • 传入的字符串要是以有标签的 以html的形式来

dom.create源代码

create(string) {
    // 创建容器    template标签可以容纳任意元素
    const container = document.createElement('template')
    // 要trim,防止拿到空字符
    container.innerHTML = string.trim()
    // 必须要 使用 .content  要不然拿不到
    return container.content.firstChild

    // 或者
    // container.innerHTML = string
    // return container.content.children[0]
  }

示例

const div = dom.create('<div><span>hello</span></div>')
console.log(div);

dom.after 函数

由于dom中的api由于只有添加前面的节点的方法,如果想要往某个节点的后面插入节点非常的费劲

dom.after(node,newNode)     // 用于新增下一个(弟弟)节点

dom.after 源代码

这里不比担心 如果 node 这个节点是最后一个节点怎么办。 即使为null,依然能插入的

 after(node, newNode) {
    // 找到此节点的爸爸然后调用insertBefore(插入某个节点的前面)方法,
    //把 newNode 插入到下一个节点的前面
    return node.parentNode.insertBefore(newNode, node.nextSibling)
  }

示例

dom.before 函数

这个方法和 dom.after 方法思路一致,

dom.before(node,node2)   // 用于新增上一个(哥哥)节点

dom.before 源代码

before(node, newNode) {
    // 正常的返回DOM原生的添加前面的节点的方法即可
    return node.parentNode.insertBefore(newNode, Node)
  }

dom.append 函数

dom.append(parent,child)  // 用于新增儿子节点

dom.append 源代码

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

dom.wrap函数

实现思路: 先把这个节点先从DOM树中移出来,把原来的位置插入新的(爸爸)节点,然后在这个爸爸节点中插入原来的节点

dom.wrap(`<div></div>`)   // 用于添加在此节点外面套一个节点   (爸爸)节点

dom.wrap 源代码

  wrap(node, newNode) {
    // 先把newNode放在node节点的前面   后面也行
    dom.before(node, newNode)
    // 然后把node节点放在newNode里面
    dom.append(newNode, node)
  }

实现思路图:
dom.wrap思路图

示例:
示例

删除节点

  • dom.remove(node) // 用于删除节点
    
  • dom.empty(parent)  // 用于删除后代
    

dom.remove 函数

用法: 删除某一个节点,并返回这个节点

思路 : 让他的爸爸删除他的儿子。

源代码

  remove(node) {
    node.parentNode.removeChild(node)
    //返回删除的节点
    return node
   }

dom.empty 函数

用法: 删除这个节点的所有子代

思路: 遍历删除它的所有子节点,并返回 删除的节点

不能用for循环的原因:因为每次 dom.remove 删除的时候,它的长度就会随之改变, 而我们又在for循环它,我测试时候会出现bug,因此我们选择使用 while 循环 解决。

源代码

  empty(node) {
    const firstChild = node.firstChild
    while (firstChild) { 
      array.push(dom.remove(node.firstChild))
    }
    // 返回删除的节点
    return array
  }

示例

删除节点

修改

修改

  • dom.attr(node,'title','hello')  //将node节点中的 title 属性值改为 hello    用于读写属性,,
    dom.attr(node,'title')  // 获取 node节点的title属性值
    
  • dom.text(node,'我是修改后的')  //将node节点中的文本修改为xxxxx    用于读写文本内容
    dom.text(node)   // 查看文本内容
    
  • dom.html(node,'<em>我是修改后的</em>')  //将node节点中的html元素内容修改为xxxx ,  用于读写html内容
    dom.html(node)  // 查看此节点中的html内容
    
  •   dom.style(node,{color:'red'})   // 用于修改style样式
    
  • dom.class.add(node,'btn')  // 用于添加class类名
    
  • dom.class.remove(node,'btn')   // 用于删除class
    
  • dom.class.has(node,'btn')  // 查看是否拥有类名
    
  • dom.on(node,'click',fn) // 用于添加事件监听
    
  • dom.off(node,'click',fn)  // 用于删除事件监听
    

dom.attr 函数

用法: 如果传入的是三个参数 设置/修改 该节点中的属性值和属性名

dom.attr(node,'title','hello')		// 传入三个参数,就设置它的属性名和属性值
dom.attr(node,'title')    // 如果传入的是俩个参数    获取属性名

源代码

  attr(node, name, value) {
    if (arguments.length === 3) {
      // 设置该节点某个属性和属性值
      node.setAttribute(name, value)
    } else if (arguments.length === 2) {
      // 查看该节点某个属性的值
      return node.getAttribute(name)
    }
  }

示例

示例
示例

dom.text 函数

用法 和 innerText一致

源代码

  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
      }
    }
  }

示例

dom.text示例

dom.html 函数

用法和 innerHTML 一致

源代码

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

dom.style 函数

原生DOM操作style的写法是:

 xxx.style.color = 'red'  或者  xxx.style['color']  = 'red'  

我封装的修饰style 的写法:

dom.style(xxx,{color:'red'})

思路: 我们可以引用原生的第二种写法 ,遍历属性值key‘,用xxx.style[key]实现 , 此时的color 是一个变量需要从我们传入的对象中获取。 为什么不用 style.color=‘red’ 的原因是因为 对象的属性值是字符串。

源代码

style(node, object) {
    for (let key in object) {
      // 不能用 style.key 是因为 key是变量
      node.style[key] = object[key]
    }
  }

示例

使用对象的形式来修改样式:
示例1

使字符串的形式来修改样式
示例2

dom.class 函数

用法:

dom.class.add(test, 'red')   // 添加类名
dom.class.remove(test, 'red')   //  删除类名
console.log(dom.class.has(test, 'red'));   // 查看类名

思路:首先在class 对象中 创建三个不同功能的方法。 然后封装一下原生的DOM即可。

源代码

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

示例

示例1

dom.on 函数 和 dom.off 函数

用法:

dom.on(test, 'click', fn)  // fn是函数,  添加了点击事件
dom.off(test,'click',fn) // 移除点击事件

思路: 封装一下原生的DOM的addEventListener即可。

源代码

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

示例

示例1

查 (获取元素)

  • dom.find('选择器')  // 用于获取标签或标签们
    
  • dom.parent(node)  // 用于获取父元素
    
  • dom.children(node)  // 用于获取子元素
    
  • dom.siblings(node)  //用于获取兄弟姐妹元素
    
  • dom.next(node)  // 用于获取弟弟
    
  • dom.previous(node)  // 用于获取哥哥
    
  • dom.each(nodes,fn)  // 用于遍历所有节点
    
  • dom.index(node)  // 用于获取索引值为x 的元素
    

dom.find 函数

用法 和 document.querySelectorAll() 一致

源代码

 find(selector) {
    return document.querySelectorAll(selector)
  }

示例:

dom.find()示例

dom.find()示例2

dom.parent 函数 和 dom.children 函数

用法:

dom.parent(node)  // 用于获取父元素
dom.children(node)  // 用于获取子元素

源代码

 parent(node) {
    return node.parentNode
  },
      
  children(node) {
    return node.children
   }

dom.siblings 函数

用法:

dom.siblings(node)  //用于获取兄弟姐妹元素 (除了自己)

思路: 用 node.parentNode.children 获取所有子元素, 因为获取的伪数组, 转换为数组后过滤掉自己。

源代码

  siblings(node) {
    return Array.from(node.parentNode.children)
      .filter(n => n !== node)
  }

dom.parent 、dom.children 、 dom.siblings 示例

dom.parent  、dom.children  、 dom.siblings  示例

dom.next 函数 和 dom.previous 函数

用法:

dom.next(node)  // 用于获取下一个节点       (弟弟)节点
dom.previous(node)   // 用于获取上一个节点      (哥哥)节点

思路: 我们使用 node.nextSibing 时 可以会返回 文本节点, 这时我们需要排除文本节点即可 (nodeType 为1是元素节点、为3 是文本节点) 、 dom.previous的思路也是一样的

源代码

 next(node) {
    let x = node.nextSibling
    while (x && x.nodeType === 3) { //  1是元素节点, 3是文本节点
      x = x.nextSibling
    }
    return x
  },
  previous(node) {
    let x = node.previousSibling
    while (x && x.nodeType === 3) { //  1是元素节点, 3是文本节点
      x = x.previousSibling
    }
    return x
  }

示例

dom.next示例

dom.previous示例

dom.each 函数

用法: 遍历每个元素,使他做对应的事

dom.each(nodes,fn)  // 用于遍历所有节点

源代码

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

示例:

dom.each

dom.index 函数

用法: 返当前节点在父节点内的索引值

dom.index(node)  // 返回当前节点在它父节点中的索引值

思路: 获取自己的父节点的所有子节点, 然后遍历, 如果遍历的子节点是自己,就返回 i

源代码

  index(node) {
    const list = dom.children(node.parentNode)
    for (let i = 0; i < list.length; i++) {
      if (list[i] === node) {
        return i
      }
    }
  }

示例:

dom.index

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值