源码

一、“增”的 API

1. dom.create(
hi
) (用于创建节点)

  • 最简单的一步:

    image.png

  • 或者直接写到上面的 window.dom 里面

image.png删掉 : 和 function 意思是一样的
window.dom = {
//create:function(){}
create(){}
}

1.1.先试着只是添加标签名

  1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597909473591-6a079a82-952b-4ca2-9776-c60c0b370858.png#align=left&display=inline&height=100&margin=%5Bobject%20Object%5D&name=image.png&originHeight=199&originWidth=516&size=17872&status=done&style=none&width=258)dom.js 里已经写好了,那么来尝试着在 main.js 里用一下<br /> ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597909537310-1e0b6fab-be03-44c3-aa73-18effeb9c338.png#align=left&display=inline&height=60&margin=%5Bobject%20Object%5D&name=image.png&originHeight=119&originWidth=440&size=10245&status=done&style=none&width=220) ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597909576661-61823804-4361-4959-b8cf-edf49498190f.png#align=left&display=inline&height=62&margin=%5Bobject%20Object%5D&name=image.png&originHeight=124&originWidth=543&size=13722&status=done&style=none&width=271.5)<br />原因是:没有在index.html里面引用dom.js<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597910281087-831f4d5e-b427-4a32-ac3e-6dbe5a5a8f40.png#align=left&display=inline&height=37&margin=%5Bobject%20Object%5D&name=image.png&originHeight=73&originWidth=492&size=9841&status=done&style=none&width=246)注意引入顺序,要先引入 dom.js 否则还是会说 dom 未定义<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597910345054-dd37fad0-6332-4610-b6e6-ba87318b5cc4.png#align=left&display=inline&height=46&margin=%5Bobject%20Object%5D&name=image.png&originHeight=92&originWidth=257&size=2710&status=done&style=none&width=128.5)这样显示标签名就添加成功了<br />但是引出了一个问题 ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597909756536-3f8f840b-7257-4ef8-a46d-09a209977e48.png#align=left&display=inline&height=83&margin=%5Bobject%20Object%5D&name=image.png&originHeight=165&originWidth=503&size=117016&status=done&style=none&width=251.5) 用下面的方法来实现

1.2.实现可以直接创建 <div><span>1</span></div>

  1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597910860070-8af60b0b-0bfc-4606-99ff-bbaa0cad9809.png#align=left&display=inline&height=171&margin=%5Bobject%20Object%5D&name=image.png&originHeight=342&originWidth=827&size=46701&status=done&style=none&width=413.5)<br />这种写法有一个bug,里面不能是 td ,会显示undefined ,因为这里的容器是div,里面不允许有td元素
  • template — 专门用来容纳任意元素

并且 table 元素不能用children[] 来获取

  1. window.dom = {
  2. create(string){
  3. const container = document.createElement('template') //容器
  4. container.innerHTML = string.trim() //trim() 用来去掉前后空格
  5. return container.content.firstChild
  6. }
  7. }

image.png
string 后面不加 trim() 的结果:会选择前面的空格作为字符串显示出来
image.png
最后对于最终结果的总结:
image.pngtd元素不能用children[0] 获取

2. dom.after(node,node2) (用于新增弟弟)

插入到该节点的后面

  1. after(node, node2) {
  2. node.parentNode.insertBefore(node2, node.nextSibling)
  3. }
  • 需要先找到该节点node的下一个节点,然后在node的下一个节点前面添加新节点 node2
  • 关于 insertBfore 在 DOM 编程笔记里并没有涉及

实践效果图:
image.png

3. dom.before(node,node2) (用于新增哥哥)

这个是支持的

  1. before(node, node2) {
  2. node.parentNode.insertBefore(node2, node)
  3. }

图示:
image.pngnewDiv 就到前面去了

4. dom.append(parent,child) (用于新增儿子)

  1. append(parent,node){
  2. parent.appendChild(node)
  3. }

图示:
image.png

5. dom.wrap(node,parent) (用于新增爸爸)

  1. wrap(node,parent){
  2. dom.before(node,parent)
  3. //先添加新节点到原先节点前面
  4. dom.append(parent,node)
  5. //再把原先的节点添加为新节点的儿子
  6. }
  • 写法是dom.before 和 dom.append 的结合

图解该写法:
image.png
图示:
image.pngimage.png

二、“删”的 API

1. dom.remove(node) (用于删除节点)

  1. remove(node){
  2. // node.remove() //该方法比较新,有可能会用不了
  3. node.parentNode.removeChild(node) //先找到父元素,再从父元素里删除该节点
  4. return node
  5. }

2. dom.empty(parent) (用于删除后代)

  • [x] 方法一:直接让元素后代为空,无法获取到删除的节点

    1. empty(node){
    2. node.innerHTML = '' //直接设置元素后代为空
    3. //上面的方法也可行,但是如果想获取到里面的节点的引用,可用下面的方法(返回移除的节点)
    4. }
  • [x] 方法二:

    1. empty(node){
    2. // node.innerHTML = '' //直接设置元素后代为空
    3. //上面的方法也可行,但是如果想获取到里面的节点的引用,可用下面的方法(返回移除的节点)
    4. const array = []
    5. let x = node.firstChild
    6. while(x){
    7. array.push(dom.remove(node.firstChild))
    8. x = node.firstChild
    9. }
    10. return array
    11. },

    得到如下结果:
    image.png

  • [ ] 为什么会有七个节点,这个上节课讲过,用 childNodes.length 会把空格也算一个文本节点

  • image.png

—————————————————————可略———————————————————-

  • 方法不可行:删除后代,并获取后代(实践证明这个方法是错误的)
    1. empty(node){
    2. //const childNodes = node.childNodes
    3. const {childNodes} = node //获取到所有的子节点,等价于上面那句
    4. const array = []
    5. for(let i=0;i<childNodes.length;i++){
    6. dom.remove(childNodes[i])
    7. array.push(childNodes[i])
    8. }
    9. return array
    10. }
    示例:
    image.png
    说明该方法有问题:打出每次的 childNodes.length
    image.png才明白每删除一个i, childNodes.length 就发生了变化,所以这里不能使用for循环
    —————————————————————可略———————————————————-

三、“改”的 API

1. dom.attr(node,'title',?) (用于读写属性)

attr - attribute 的缩写
node - 节点
‘title’ - 属性名
? - 是要改成的结果(属性值)

  • DOM 封装里的内容

    1. attr(node, name, value) { // 重载
    2. if (arguments.length === 3) {
    3. //如果长度为3,设置节点(实现写)
    4. node.setAttribute(name, value)
    5. } else if (arguments.length === 2) {
    6. //如果长度为2,获取节点(实现读)
    7. return node.getAttribute(name)
    8. }
    9. }

    重载:根据参数个数,写不同的代码就是重(chong)载

  • 实际应用:

    1. dom.attr(test,'title','Hi,I am Frank') //实现写属性
    2. const title = dom.attr(test,'title') //实现读取该属性
    3. console.log(`title:${title}`)

    image.png这个 div 就得到了一个名为title的属性

    2. dom.text(node,?) (用于读写文本内容)

  • DOM 封装里的内容

    1. text(node,string){ // 适配
    2. if('innerText' in node){
    3. node.innerText = string // IE
    4. }else{
    5. node.textContent = string // firefox | Chrome
    6. }
    7. }

    上面只能写,那么怎么让他能只读又能改写内容呢,同上一种,加重载

    1. text(node, string) {
    2. // 适配
    3. if (arguments.length === 2) {
    4. if ('innerText' in node) {
    5. node.innerText = string // IE
    6. } else {
    7. node.textContent = string // firefox | Chrome
    8. }
    9. } else if (arguments.length === 1) {
    10. if ('innerText' in node) {
    11. return node.innerText // IE
    12. } else {
    13. return node.textContent // firefox | Chrome
    14. }
    15. }
    16. }
  • 实际应用:

    1. dom.text(test,'你好,这是新的内容')

    image.png

  • [x] 这里有一个问题,如果test里还有p标签等别的内容,会全部被覆盖掉(这就看具体要怎么使用了)

    3. dom.html(node,?) (用于读写 HTML 内容)

  • DOM 封装里的内容

    1. html(node, string) {
    2. if (arguments.length === 2) {
    3. node.innerHTML = string
    4. } else if (arguments.length === 1) {
    5. return node.innerHTML
    6. }
    7. }

    4. dom.style(node,{color:'red'}) (用于修改 style)

    只写的话下面的代码就可以解决

  • DOM 封装里的内容

    1. style(node,object){
    2. for(let key in object){
    3. // key: border / color
    4. // node.style.border = ...
    5. // node.style.color = ...
    6. node.style[key] = object[key]
    7. }
    8. }
  • [x] 变量做 key ,就必须把 key 放到 [] 里,不能直接 .key

  • image.png
  • 实际应用:

    1. dom.style(test, { border: '1px solid red', color: 'blue' })

    image.png

    那么分读写怎么解决呢

  • DOM 封装里的内容

    1. style(node, name, value) {
    2. if (arguments.length === 3) {
    3. // dom.style(div, 'color', 'red')
    4. node.style[name] = value
    5. } else if (arguments.length === 2) {
    6. if (typeof name === 'string') {
    7. // dom.style(div,'color')
    8. return node.style[name]
    9. } else if (name instanceof Object) {
    10. // dom.style(div, {color: 'red'})
    11. const object = name
    12. for (let key in object) {
    13. node.style[key] = object[key]
    14. }
    15. }
    16. }
    17. }
  • [x] typeof 返回类型

  • instanceof检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
  • 分别对应下面三种调用方式

    1. dom.style(test, { border: '1px solid red', color: 'blue' })
    2. dom.style(test, 'border')
    3. dom.style(test, 'border', '1px solid red')

    5. dom.class.add(node,'blue') (用于添加 class)

    6. dom.class.remove(node,'blue') (用于删除 class)

  • DOM 封装里的内容

    1. //添加、删除 class,以及检查该元素是否存在
    2. class: {
    3. add(node, className) {
    4. node.classList.add(className)
    5. },
    6. remove(node, className) {
    7. node.classList.remove(className)
    8. },
    9. contains(node, className) { // 检查是否有该元素
    10. return node.classList.contains(className)
    11. },
    12. }
  • [x] 不加 return 返回值就是 undefined

  • classList 检查该元素是否存在用 contains 而不是 has(直接搜 classList mdn)
  • 实际应用:

    1. //添加 class
    2. dom.class.add(test, 'red')
    3. dom.class.add(test, 'blue')
    4. //删除 class
    5. dom.class.remove(test, 'blue')
    6. //检查该元素是否存在
    7. console.log(dom.class.contains(test, 'blue')) // false

    7. dom.on(node,'click',fn) (用于添加事件监听)

    8. dom.off(node,'click',fn) (用于删除事件监听)

  • DOM 封装里的内容

    1. //添加、删除事件监听
    2. on(node, eventName, fn) {
    3. node.addEventListener(eventName, fn)
    4. },
    5. off(node, eventName, fn) {
    6. node.removeEventListener(eventName, fn)
    7. }
  • [x] 要给函数取个名字,不然没法删除掉

  • 实际应用:

    1. //添加、删除事件监听
    2. const fn = ()=>{
    3. console.log('点击了')
    4. } //要给函数取个名字,不然没法删除掉
    5. dom.on(test,'click',fn)
    6. dom.off(test, 'click',fn)

    四、“查”的 API

    1. dom.find('选择器') (用于获取标签或标签们)

  • DOM 封装里的内容

    1. find(selector){
    2. return document.querySelectorAll(selector)
    3. }
  • 实际应用: ```javascript //获取标签或标签们 const testDiv = dom.find(‘#test’)[0] console.log(testDiv)

// 获取对应的元素(不用下面两个参数的方法也可以获取到对应的元素) const div = dom.find(‘#test>.red’)[0]

  1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597938409099-5ff91768-1b97-4fb2-a87a-5dbe7ff1629f.png#align=left&display=inline&height=38&margin=%5Bobject%20Object%5D&name=image.png&originHeight=76&originWidth=747&size=11030&status=done&style=none&width=373.5)
  2. - [x] 注意加上后面的 [0] 否则不是获取到数组里的第一个元素,而是NodeList
  3. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1597938552711-e9118382-1b1b-4f0c-80fe-c33134babde0.png#align=left&display=inline&height=54&margin=%5Bobject%20Object%5D&name=image.png&originHeight=107&originWidth=346&size=6085&status=done&style=none&width=173)
  4. <a name="pI0V2"></a>
  5. #### 当给 find 确定个范围应该怎么写呢(用两个参数)
  6. - DOM 封装里的内容
  7. ```javascript
  8. find(selector,scope){
  9. return (scope || document).querySelectorAll(selector)
  10. //如果有scope,就在scope里找selector;否则就在document里找
  11. }
  • 实际应用:

    1. //获取标签或标签们
    2. const testDiv = dom.find('#test')[0]
    3. console.log(testDiv)
    4. const test2 = dom.find('#test2')[0]
    5. console.log(dom.find('.red',test2)[0]) //获取test2里的含有class = 'red' 的元素

    image.png

    2. dom.parent(node) (用于获取父元素)

  • DOM 封装里的内容

    1. //获取父元素
    2. parent(node){
    3. return node.parentNode
    4. }
  • 实际应用:

    1. //获取父元素
    2. console.log(dom.parent(test))

    image.png

    3. dom.children(node) (用于获取子元素)

  • DOM 封装里的内容

    1. children(node) {
    2. return node.children
    3. }

    4. dom.siblings(node) (用于获取兄弟姐妹元素)

  • DOM 封装里的内容

    1. children(node) {
    2. return Array.from(node.parentNode.children).filter(n=>n!==node)
    3. }
  • [x] 先找到该元素的爸爸,再找他爸爸的子元素

  • .filter 是获取数组的通过测试的元素组成的数组,不改变原数组
  • 因为获取到的是个NodeList(一个类似数组的对象,也就是伪数组),所以需要先用 Array.from() 转成数组
  • 实际应用:

    1. //获取兄弟姐妹元素
    2. console.log(dom.siblings(dom.find('#s2')[0]))
  • [x] 注意后面的 [0] 不能省略

image.png

5. dom.next(node) (用于获取弟弟)

image.png
结果返回的弟弟是个回车

  • DOM 封装里的内容

    1. next(node){
    2. let x = node.nextSibling
    3. while(x && x.nodeType === 3){
    4. x = x.nextSibling
    5. }
    6. //x存在且直到第一个x的节点类型不为3(文本节点),返回x
    7. return x
    8. }
  • 实际应用:

    1. //获取弟弟
    2. console.log(dom.next(dom.find('#s2')[0]))

    image.png

    6. dom.previous(node) (用于获取哥哥)

    上面的 next => previous 其他完全一样

  • DOM 封装里的内容

    1. next(node){
    2. let x = node.previousSibling
    3. while(x && x.nodeType === 3){
    4. x = x.previousSibling
    5. }
    6. //x存在且直到第一个x的节点类型不为3(文本节点),返回x
    7. return x
    8. }
  • 实际应用:

    1. //获取弟弟
    2. console.log(dom.previous(dom.find('#s2')[0]))

    image.png

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

  • DOM 封装里的内容

    1. each(nodeList,fn){
    2. for(let i=0; i<nodeList.length; i++){
    3. fn.call(null,nodeList[i])
    4. }
    5. }
  • 实际应用:

    1. //遍历所有节点
    2. const t = dom.find('#travel')[0] //先找到要遍历的节点
    3. dom.each(dom.children(t),(n)=>dom.style(n,'color','red'))
    4. //找到子节点,并且在每个子节点上加一个style

    image.png

    8. dom.index(node) (用于获取排行老几)

  • DOM 封装里的内容

    1. index(node) {
    2. //node.parentNode.children
    3. const list = dom.children(node.parentNode)
    4. let i
    5. for (i = 0; i < list.length; i++) {
    6. if (list[i] === node) {
    7. break
    8. }
    9. }
    10. return i
    11. }
  • [x] 先获取到它的兄弟姐妹,第一种写法容易有问题,所以这里使用了其他的写法

  • 遍历 list ,直到 list 等于 node 停止,返回这个i
  • 实际应用:
    1. //获取排行老几
    2. console.log(dom.index(s2))
    image.png length 为1, 排行第二