手写一个DOM库
原生DOM接口不太好用,所以今天自己来封装一个DOM库
源代码链接:https://gitee.com/kilok/dom-by-hand
提供一个全局的window.dom对象
接下来对“增删改查”进行说明
一、增加元素、节点
dom.create(string)创建节点
原生api需要用createElement(‘div’),div.innerText = 'hello’这两句话
create(string){
const container = document.createElement('template')
container.innerHTML = String.prototype.trim.call(string)//去除多余空格
// console.log(container.content)
return container.content.firstChild
},
封装后只需dom.create(’<div>‘hello’</div>’)
after(node,newNode)新增弟弟节点
DOM只提供insertBefore接口,故在node.nextSibling前插入
after(node,newNode){
return node.parentNode.insertBefore(newNode,node.nextSibling)
}
before(node,newNode)新增哥哥节点
before(node,newNode){
return node.parentNode.insertBefore(newNode,node)
}
append(parent,node)新增儿子节点
append(parent,node){
return parent.appendChildren(node)
}
wrap(node,newParent) 用于新增爸爸节点
思路:
- 将newparent放到node前
- 再将node append到newParent里
wrap(node,newParent){
node.before.call(node,newParent)
newParent.append(node)
}
二、删除元素、节点
remove(node)删除节点
remove(node){
node.parentNode.removeChild(node)
return node
}
empty(node)删除后代
只要有子节点就不断遍历
empty(node){
const array = []
let x = node.firstChild
while(x){
array.push(dom.remove(node.firstChild))
x = node.firstChild
}
}
三、修改、读取属性和文本内容
attr(node,name,value)读写属性
重载:函数名相同,参数类型/数量不同做不一样的处理
当只有两个参数时,就读属性,返回属性
当有三个参数时,就修改属性
这里由于同时可以读写属性,用到了getter和setter设计模式
attr(node,name,value){
if(arguments.length === 3){
node.setAttribute(name,value)
}else if(arguments.length === 2){
return node.getAttribute(name)
}
}
text(node,string)读写文本内容
text(node,string){
if(arguments.length === 1){ //读
if('innerText' in node){//IE
return node.innerText
}else{
return node.textContent //firefox or chrome
}
}else if(arguments.length ===2){ //写
if('innerText' in node){//IE
node.innerText = string
}else{
node.textContent = string //firefox or chrome
}
}
},
html(node,string)读写html内容
html(node,string){
if(arguments.length ===1 ) {
return node.innerHTML
}else if(arguments.length ===2 ){
node.innerHTML = string
}
},
style(node,name,value)修改读取style
大体思路:node.style.color = ‘red’
有三种情况
-
dom.style(node,‘color’)读取color属性
-
dom.style(node,‘color’,‘red’)设置color
-
dom.style(node,{‘color’:‘red’,‘border’:‘1px solid blue’}设置多个style属性
使用适配器模式 -
有3个参数,直接第二种情况dom.style.color = ‘red’
-
有2个参数,需要判断第二个参数(typeof xxx === ‘string’)
style(node,name,value){
if(arguments.length === 3 ){
// dom.style(node,'color','red')
node.style[name] = value
}
if(arguments.length === 2){
if(typeof name === 'string'){
// dom.style(node,'color')
return node.style[name]
}else if(name instanceof Object){//instanceof 判断name 是否为Object
// dom.style(node,{'color':'red','border':'1px,solid,red'})
let object = name
for (let key in object){
node.style[key] = object[key]
}
}
}
},
思路node.classList.add(className)
dom.class.add/remove/has()添加,删除,是否包含一个类
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,eventName,fn)添加事件监听
on(node,eventName,fn){
node.addEventListener(eventName,fn)
}
dom.remove()删除事件监听
off(node,eventName,fn){
node.removeEventListener(eventName,fn)
}
四、获取标签、元素
dom.find(selector,scope)获取标签或标签们
find(selector,scope){
return (scope || document).querySelectorAll(selector)
}
dom.parent(node)获取父元素
parent(node){
return node.parentNode
}
dom.children(node)获取子元素
children(node){
return node.children
}
dom.siblings(node)获取兄弟元素
思路:找到爸爸节点后删掉自己
siblings(node){
return Array.from(node.parentNode.children).filter(n=>n!==node)
},
dom.next(node)获取弟弟节点
节点包含html标签、文本、注释,需要确保获取到的是html标签
next(node){
let x = node.nextSibling
//如果下一个节点不是html元素节点则继续找下一个
while(x && x.nodeType !== 1){ //nodeType返回值1为元素节点,2为属性节点
x = x.nextSibling
}
return x
},
dom.prev(node)获取哥哥节点
prev(node){
let x = node.previousSibling
while(x && x.nodeType !==1){
x = x.previousSibling
}
return x
},
dom.each(nodeList,fn)遍历所有节点
each(nodeList,fn){
for(let i =0;i<nodeList.length;i++){
fn.call(null,nodeList[i])
}
},
index(node)用于获取排行第几
index(node){
let nodeList = dom.children(dom.parent(node))
for(let i=0;i<nodeList.length;i++){
if(nodeList[i] === node){
return i
}
}
return -1 //没找到
}