DOM封装

77 阅读3分钟

index.html

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>DOM1</title>
    <style>
      .red {
        background: red;
      }
    </style>
  </head>
  <body>
    示例
    <div>
      <div id="test">
        <span>test1</span>
        <p class="red">段落标签3</p>
        test2
      </div>
      <div id="test2">
        <p class="red">段落标签3</p>
        test2
      </div>
    </div>

    <div id="empty">
      <div id="e1"></div>
      <div id="e2"></div>
      <div id="e3"></div>
    </div>

    <div id="siblings">
      <div id="s1"></div>
      <div id="s2"></div>
      <div id="s3"></div>
    </div>

    <div id="travel">
      <div id="t1">t1</div>
      <div id="t2">t2</div>
      <div id="t3">t3</div>
    </div>

    <script src="dom.js"></script>
    <script src="main.js"></script>  //引入dom.js,再执行main.js
    </body>
</html>

dom.js

window.dom = {
  create(string) {
      const container = document.createElement('template'); //temeplate专门用来容纳任意元素
      container.innerHTML = string.trim(); //trim()把字符串两边的空格去掉
      return container.content.firstChild;
  },
  after(node,node2) {
      node.parentNode.insertBefore(node2, node.nextSibling); 
      //找到这个节点的爸爸,调用这个爸爸的insertBefore的方法,把node2插到node下一个节点的前面
  },
  before(node, node2){
      node.parentNode.insertBefore(node2, node);
  },
  append(parent, node){
      parent.appendChild(node)
  },
  wrap(node, parent){
      dom.before(node,parent)
      dom.append(parent, node) 
      //把parent放到节点的前面,然后新建一个parent2放在这个节点的里面
  },
  remove(node){
      node.parentNode.removeChild(node)
      return node //保留这个节点的引用
  }, //删除节点,并返回节点(返回移除对象)
  
  empty(node){
      const array = []
      let x = node.firstChild
      while(x){
        array.push(dom.remove(node.firstChild)) 
        x = node.firstChild  //其实是当时的第二个节点  
        //当X存在,就把它移除放到数组里面
      }
      return array
    },
    
    attr(node,  name, value){  //重载
        if(arguments.length === 3){
            node.setAttribute(name, value)
        }else if(arguments.length === 2){
            return node.getAttribute(name)
        }
    }, //如果这个参数内容的长度为3,我就返回设置名字和值;如果为2,我就获取他的名字。
    
    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
      }
    }
  },
  
  html(node, string){  //重载
      if(arguments.length === 2){
          node.innerHTML = string 
      }else if(arguments.length === 1){
          return node.innerHTMl
      }
  },
  style(node, name, value){
      if(arguments.length === 3){
          //dom.style(div, 'color', 'red')
          node.style[name] =value  //node.style对应的name等于value
      }else if(arguments.length === 2){
          if(typeof name === 'string'){ //name的类型是字符串
              //dom.style(div, 'color')
              return node.style[name]
          }else if(name instanceof Object){  //name是Object的实例
              //dom.style(div, {color: 'red'})
              const object = name
              for(let key in object){
                  node.style[key] = object[key]
              }
          }
      }
  },
  class: {
      add(node, className){
          node.classList.add(className)
      },
      remove(node, className){
          node.classList.remove(className)
      },
      has(node, className){
          return node.classList.contains(className)
      }
  },
  on(node, eventName, fn){
      node.addEventListener(eventName, fn) //fn是一个函数
  },
  off(node, eventName, fn){
      node.removeEventListener(eventName, fn)
  },
  find(selector, scope){  //scope是范围的意思
    return (scope || document).querySelectorAll(selector)
  },
  //如果有scope,我就在scope里面调用querySelectorAll;如果没有,我就在document里面调用
  parent(node){
      return node.parentNode
  },
  children(node){
      return node.children
  },
  siblings(node){  ///此节点不能包括自己
      return Array.from(node.parentNode.children).filter(n=>n!==node)
  },  
  //在这个伪数组变成数组之后,进行一个过滤,只有那个元素不等于当前这个节点,我就把它放在数组里面
  next(node){
      let x = node.nextSibling  //让x等于下一个节点
      while(x && x.nodeType === 3){
          x = x.nextSibling
      }
      return x
  }, //如果x存在,x是不是文本,如果是文本就再下一个,如果不是文本,就返回x。如果为空,也得返回。
  previous(node){
      let x = node.previousSibling
      while(x && x.nodeType === 3){
          x = x.previousSibling
      }
      return x 
  },
  each(nodeList, fn){
      for(let i=0;i<nodeList.length;i++){
          fn.call(null, nodeList[i])
      }
  },
  index(node){
      const list = dom.children(node.parentNode) //获取它爸爸的儿子
      let i 
      for(i=0;i<list.length;i++){
          if(list[i] === node){
              break
          }
      }
      return i
  }
        
};

注意

注释

window.dom = {};
dom.create = function(){};
//等价于
window.dom = {
   create = function(){}
    }
//等价于
window.dom = {
   create (){}
   }
  • temeplate不能直接用return container.children,必须加上return.container.content.firstChild

  • const childNodes = node.childNodes 等价于 const {childNodes} = node

  • array.push(dom.remove(node.firstChild)) 等价于 dom.remove(node.firstChild)+ array push(node.firstChild)

  • attr:是attribute的缩写

  • innerText是支持IE的;innerContent是支持Chrome和Firefox的;所有浏览器都同时支持两种

  • 重载:既可以读又可以写

  • node.parentNode.children是个伪数组,不能直接跟filter,

main.js

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

dom.after(test, div); //把test插到div前面

const div3 = dom.create('<div id="parent"></div>')
dom.wrap(test, div3) //把div3包到test的外面


const nodes = dom.empty(window.empty)
console.log(nodes)

dom.attr(test, 'title', 'Hi, I am Frank') //3个参数实现写
const title = dom.attr(test, 'title') //2个参数实现读
console.log(`title: ${title}`) //打印title的值

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

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

dom.class.add(test, 'red')
dom.class.add(test, 'blue')
dom.class.remove(test, 'blue')
console.log(dom.class.has(test,'blue'))

const fn = ()=>{
    console.log('点击了')
}  //创建一个函数
dom.on(test, 'click', fn)
dom.off(test, 'click', fn)

const testDiv = dom.find('#test')[0]
console.log(testDiv)
const test2 = dom.find('#test2')[0]
console.log(dom.find('.red', test2)[0])  //获得是多个节点,加个[0]

console.log(dom.parent(test))

const s2 = dom.find('#s2')[0]
console.log(dom.siblings(s2))
console.log(dom.next(s2))
console.log(dom.previous(s2))

const t = dom.find('#travel')[0]
dom.each(dom.children(t), (n)=> dom.style(n, 'color', 'red'))
//找到这个t的所有children,给它一个each操作,给它一个n占位,n的color为red。
console.log(dom.index(s2))

注意:

  • 打开终端---输入parcel src/index.html---打开服务器

总结

什么叫封装

1.png

5.png

术语

库,API,框架

6.png

对象风格

2.png

3.png

4.png