DOM封装(自己写一个DOM库)

本文介绍了作者在学习DOM原生API过程中,为解决API不便使用的问题,自封装的一套DOM库。涵盖增加节点(创建、新增弟弟、哥哥、儿子、爸爸)、删除节点及后代、修改节点属性、文本、HTML内容、样式、class以及事件监听,还包括查找节点(获取标签、父元素、子元素、兄弟姐妹、弟弟、哥哥)等功能。总结中强调了自定义库的灵活性和易用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

DOM提供的api实在是不好用,各种各样的都有,需要记住的又多,特性不一,还要考虑兼容性问题。
在学习DOM原生api的同时,自己封装一套好用的api,即把原生api学会了,又做出一套好用的api;

首先是把声明一个dom全局变量,并挂载在window上。
dom = window.dom
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一,增加节点

创建节点dom.create(<div>hi</div>)
create(string) {
    const container = document.createElement("template");
    container.innerHTML = string.trim();
    return container.content.children[0];
  }

思路:用一个template标签,把传进来的内容包裹住,再返回需要创建的标签。
应为可能创建的标签是全部,比如li标签,div不能直接包裹住li标签。
所以template是最合适的。

新增弟弟dom.after(node, node2)
after(node, node2) {
    node.parentNode.insertBefore(node2, node.nextSibling);
  }

先来熟悉原生api

  • insertBefore(node1, node2):把node1插入到node2前面
  • node.nextSibling:node的下一个兄弟节点

思路:找到父节点,插入到node后面兄弟节点的前面

新增哥哥 dom.before(node, node2)
before(node, node2) {
    node.parentNode.insertBefore(node2, node);
  },

思路:找到父节点,插入到node节点前面

新增儿子 dom.append(parent, child)
append(parent, node) {
    parent.appendChild(node);
  }

这个差不多只是把单词字母变少了,和原生的api没有本质区别

新增爸爸 dom.wrap(<div></div>)
wrap(node, parent) {
    dom.before(node, parent);
    dom.append(parent, node);
  },

代码很精简,但是理解起来不是很好理解。
举个栗子node1->node2
现在来一个node3,要求不顺序变成node1->node3->node2
思路:把node3插入到node2前面,node2和node3变成兄弟节点,再把node2变成node3的子节点。

删除

删除节点 dom.remove(node)
remove(node) {
    node.parentNode.removeChild(node);
    return node;
  }

思路:找到node的爸爸,删除node
最后返回node的引用,如果不返回,node就是在内存中真正的消失了。

删除后代 dom.empty(parent)
empty(node) {
    const array = [];
    let x = node.firstChild;
    while (x) {
      array.push(dom.remove(node.firstChild));
      x = node.firstChild;
    }
    return array;
  },

思路:遍历删除子节点,返回引用(可能其他地方用得到)

这里就是原生api蛋疼的地方
上面代码仔细看,while循环中x = node.firstChild;因为firstChild会实时更新子元素的长度,所以循环的时候只能看x存不存在,不能用长度作为判断条件,因为长度是变化的。

三、修改

读写属性 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);
    }
  }

当只有两个参数时,就读属性,返回属性
当有三个参数时,就修改属性
原生api

  • setAttribute(name, value):给name属性设置value值
  • getAtteibute(name):获取name属性的值
读写文本内容 dom.text(node, ?)
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;
      }
    }
  }

如果只有一个参数就读文本内容,返回node.innerText
如果有两个参数,就写文本内容 node.innerText = string
if ('innerText' in node)这一步是考虑兼容性问题

读写HTML内容 dom.html(node, ?)
html(node, string) {
    if (arguments.length === 2) {
      node.innerHTML = string;
    } else if (arguments.length === 1) {
      return node.innerHTML;
    }
  }
修改stylestyle dom.style(node, {color:'red'})
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{div, 'color'}
        return node.style[name];
      }else if (name instanceof Object) {
        //dom.style(div, {color: 'red'})
        const object = name;
        for (let key in object) {
          node.style[key] = object[key];
        }
      }
    }
  }
  • dom.style(node, 'color', 'red')设置一个属性
  • dom.style(node, 'color')读一个属性
  • dom.style(node, {'color': 'red', 'border': '1px solid black'}) 传一个对象,同时设置多个属性
添加,删除和查看classclass dom.class.add(node, 'blue')dom.class.remove(node,'blue')
class: {
    add(node, className) {
      node.classList.add(className);
    },
    remove(node, className) {
      node.classList.remove(className)
    },
    has(node, className) {
      return node.classList.contains(className)
    }
  }
添加事件监听 dom.on(node, 'click', fn)
on(node, eventName, fn) {
    node.addEventListener(eventName, fn);
  }

是冒泡还是捕获可以视具体情况而定

删除事件监听 dom.off(node, 'click', fn)
off(node, eventName, fn) {
    node.removeEventListener(eventName, fn);
  }

四、查

获取标签或标签们 dom.find('选择器', scope)
find(selector, scope) {
    return (scope || document).querySelectorAll(selector);
  }

在指定区域或者全局document里找HTML标签
scope可以不传,那就是在全局查找
注意:返回的是伪数组,使用的时候把下标加上
const t = dom.find('#travel')[0]

获取父元素 dom.parent(node)
parent(node) {
    return node.parentNode;
  }
获取子元素 dom.children(node)
children(node) {
    return node.children
  }

注意:返回的是伪数组,使用的时候把下标加上

获取兄弟姐妹元素 dom.siblings(node)
sibling(node) {
    return Array.from(node.parentNode.children).filter(n => n !== node);
  }

思路:遍历父节点,再把自己删除掉即可

获取弟弟 dom.next(node)
next(node) {
    let x = node.nextSibling;
    while (x && x.nodeType === 3) {
      x = x.nextSibling;
    }
    return x;
  }

x.nodeType=3,x就是文本节点。
x && x.nodeType === 3防止是空格回车这些。
节点包括HTML标签、 文本、注释等,所以需要这样处理一下,保证我们找到的是一个HTML标签

获取哥哥 dom.previous(node)
previous(node) {
    let x = node.previousSibling;
    while (x && x.nodeType === 3) {
      x = x.previousSibling
    }
    return x;
  }
用于遍历所有节点 dom.each(nodes, fn)
each(nodeList, fn) {
    for (let i = 0; i < nodeList.length; i++) {
      fn.call(null, nodeList[i])
    }
  }
获取排行老几 dom.index(node)
index(node) {
    const list = dom.children(node.parentNode)
    let i;
    for (i = 0; i < list.length; i++) {
      if (ist[i] === node) {
        break;
      }
    }
    return i;
  }

总结

可以看出,原生api是真的难用。自己封装出来的,想怎么封装就怎么封装,名字简单,功能齐全。
附上我的GitHub链接源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值