手写DOM库

941 阅读5分钟

术语

  • 我们把提供给其他人用的工具代码叫做库
  • 比如jQuery、Underscore

API

  • 库暴露出来的函数或属性(应用编程接口)

框架

  • 当你的库变得很大,并且需要学习才能看懂,那么这个库叫做框架,比如Vue/React

两种封装风格

对象风格

  • 也叫命名空间风格
    • window.dom是我们提供的全局变量

  • document.creat(<div>hi</div>)用于创建节点
  • dom.after(node,node2)用于新增弟弟
  • dom.before(node,node2)用于新增哥哥
  • dom.append(parent,child)用于新增儿子
  • dom.wrap(<div></div>)用于新增爸爸

实操

搭建环境

1633400559(1).png

1633400594(1).png

1633400610(1).png

创建1个div

1633400855(1).png

  • window.dom是全局对象,Create 是属性名,对应着相应的函数
  • create只是方便理解的属性名字,可以任意取,但是tagName是参数名,不能变
  • create函数,可以写在里面:create: function(tagName) { return document.createElement(tagName); }
  • 也可以写在外面:dom.create = function(tagName)
  • 小结:函数包含着dom指令,函数类比成全局对象的某个属性,参数不能变,但是属性名可以自由发挥,只要调用属性的时候对的时候对得上就可以了 1633400877(1).png
  • 定义一个div,调用create属性对应的函数,函数执行dom操作,传参,参数为'div',即tagName
  • 初步实现功能,新增一个div

变化! 能不能在div里面创建span<div><span>1</span></div>

  • <div><span>1</span></div>作为参数?

1633402413(1).png

1633402583(1).png

  • 引入一个container,属性依然是创建div
  • container传入的字符传入HTML
  • 返回container的第0个对象,即div
  • 调用函数,注意此时的参数是代码组成的字符创

但是有BUG!

  • 如果输入参数,标签tr,td,无法实现效果,应为要在table标签里面

使用template标签,它是能容纳所有标签的标签

1633403212(1).png

  • string加trim(),防止参数前面有空格,拿到了文本,所以这是直接去掉字符串两边空格的
  • return的写法严格按照如图方式,否则出错

1633403401(1).png

  • console.log()参数div不影响结果

新增一个弟弟 after,

1633414765(1).png

1633414937(1).png

  • 定义after属性,函数参数node,node2,将node2放到node的下一个节点的前面
  • insert只有before,没有after
  • 找到node的父级,在node的下一个节点插入到node2的前面

如果node是最后的节点,没有下一个节点,依然可以实现功能

新增一个哥哥

1633415936(1).png

  • 思路同上,常规DOM指令,调用指令常规

新增一个儿子

1633416308(1).png

新增加一个父级元素

1633417423(1).png 1633417391(1).png

  • 如图二,要将div3,放到div2外面
  • 先将div3放在div2的前面或者后面
  • 然后将div2,放到div3里面,div2原地消失

删除节点

1633437188(1).png

  • 找到父级元素然后删除子代元素

删除后代

1633440883(1).png

  • 为了删除子代,但是返回被删除的子代,所以用到循环
  • 不能用foe循环,应为i<node.length,但是长度随着删除元素而变化,所以不能用
  • while循环:引入空数组array,用来存放被删除的子代元素
  • 引入x,让x等于node的第一个子元素
  • 进入while循环,array数组里面逐个放入node删除的第一个子元素,remove函数已经在上面写好,可以直接用
  • x 再次赋值,等于node的第一个子元素,因为第一个子元素删除,后面的元素替补作为第一个子元素
  • 循环条件while(x),当x为空,那么表明node里面的元素被删光了,循环停止
  • 返回array数组,可以查看被删除的部分,里面的节点除了标签,还有文本节点,这些文本节点是回车键

增加属性以及内容

1633484729(1).png

  • 这是详细的写法,重载
  • 如果接受三个参数,就是写入属性名,属性值
  • 如果接受两个参数,就是得到属性名

1633484850(1).png

  • 调用函数,写入title属性,属性内容
  • 引入变量title,得到title属性
  • 打印出title属性的内容

标签里面增加文本内容

1633485307(1).png

  • 但是有个问题,里面的内容会让元素的子元素全部消失
  • 所以在实际开发中,对于要更改的内容,标签里面加id,避免这种情况发生

适配版本

1633485728(1).png

  • innerText兼容IE
  • textContet适配chrome,firefox
  • 条件判断,这种方法叫做适配

读写html内容

1633486629(1).png

  • 判断适配
  • 如果2个参数,那么就是写入内容
  • 如果是一个参数,那么就返回内容,读写参数

添加样式

1633505021(1).png

  • 根据调用时提供的参数数量、类型进行的判断
  • 如果是三个参数 如dom.style(div,'color','red'),那么 写入样式
  • 如果是两个参数,分两种情况讨论,判断第二个参数数据类型
  • 如果第二个参数是字符串,如 dom.style(div,'color'),那么就是获取color属性
  • 如果第二个参数是对象,让node里面的key与对象里面的key一一对应,写入对象里面涉及的属性

添加、删除、判断class

7a03ac72e7158f1795356361932fb90.png

  • 三个功能,写在class对象里面
  • 最后一个判断,要return,返回值,调用的时候加console.log,即可判断是否有相应的属性

添加/删除事件监听

1633506585(1).png

1633506625(1).png

  • 注意调用思想

查找元素

1633506983(1).png

1633507021(1).png

  • 提供选择器,返回所有符合条件的元素组成的数组
  • 调用的时候加【0】,应为要得到对象,所以从数组里面调用

如果test里面有子元素,查找范围不同呢

image.png

  • 如果一个参数,就在document里面寻找元素
  • 如果两个参数,就在scope里面调用queryselector,||(或)用法
  • 思想:查找的节点不同

找到父级元素、子级元素

1633509109(1).png

找到同级元素

1633509167(1).png

  • 三种思想
  • 找到父级元素的子级元素
  • 过滤自己,条件是n等于传入的node,过滤掉
  • node.parentNode.children,返回的是伪数组{xxx,xxx,xxx,xxx},Array.from转化成数组
  • 调用的时候还是加【0】结尾

找到后面一个节点(弟弟)

1633510230(1).png

  • 思路;下一个节点可能是文本
  • 引入变量x等于下一个节点
  • while,条件,x存在并且x的属性是文本节点,x等于自己的下一个节点
  • 循环结束,返回x

遍历所有的节点

1633512300(1).png

1633512330(1).png

获取排行老几

1633512729(1).png

  • 注意let i的位置,考虑作用域问题