封装思路
因为在使用DOM的原生方法的时候总是感觉有很多代码特别的长不是很方便,例如addEventListener,我就想能不能把这些方法进行进一步的封装,形成一个自己可以使用的代码,这样比较的快捷及简便,在封装的过程中,还通过msdn进行了查询一些DOM的操作,找到了一些小窍门,大家可以看我具体实现中的实现方法,希望你可以在该篇文章中有所收获
具体实现
首先要先在window中封装一个对象.
window.dom = {
}
初步封装创建的DOM方法
// 传入参数为string
create(string) {
// 创建一个template包容器,这里面可以写任何的代码,但是div不可以写,所以使用的是template
const container = document.createElement("template");
// 将div的HTML设置为传入的string
container.innerHTML = string;
// 返回div的第一个子元素
return container.children[0];
}
在封装该方法的时候,一开始想使用创建包围代码的容器是div,但是发现div在包含时有些属性不支持显示,比如说td等,然后就在网上进行了资料查询,发现在template内可以包含大部分的元素.
在节点的后面插入节点的方法
after(node,node2) {
// 找到node的爸爸的节点调用,将node2插到node下一个节点的前面
node.parentNode.insertBefore(node2, node.nextSibling);
},
由于原生的DOM没有提供在节点后面插入节点的方法,只提供了insertBefore,所以我们可以通过该方法来插入到节点的后面
在节点的前面插入节点的方法
before(node,node2) {
// 由于自带的insertBefore方法,可以直接将node2插入到node的前面
node.parentNode.insertBefore(node2, node);
},
给父节点新增一个子节点
append (parent, child) {
parent.appendChild(node)
}
给子节点增加一个父节点
wrap(node, parent) {
// 把parent放在node的前面
dom.before(node, parent)
// 把node放在parent的里面
dom.append(parent, node)
}
这里用到了我们前面封装过得before和append,通过组合完成给子节点增加父节点的方法
删除单个节点的方式
// 删除节点
remove(node) {
// 找到该节点的父节点,然后删除父节点的子节点
node.parentNode.removeChild(node);
// 返回删除的节点以便引用
return node
},
在这里我们没有直接使用remove的方法,而是调用了很多浏览器都支持的,找到父节点,再删除子节点的方法
删除所有节点的方式
// 删除所有的子节点
empty(node) {
// 直接删除
// node.innerHTML = '';
// 找到所有的子节点
// const childNodes = node.childNodes;
// 以上的简写
const {childNodes} = node;
// 保存删除的数组
const array = []
/* 遍历所有子节点 !!!这里的childNodes的长度是实时变化的所以这种方式有弊端
for(let i=0;i<childNodes.length;i++) {
// 将所有找到的节点移除
dom.remove(childNodes[i]);
// 将删除了的节点保存到数组中,返回以供使用
array.push(childNodes[i])
}
return array*/
// 设置x为node的第一个子节点
let x = node.firstChild
while(x){
// remove后会有返回值,所以可以将删除的push到数组中
array.push(dom.remove(node.firstChild))
// 然后让x等于下一个子节点,直到没有子节点
x = node.firstChild
}
// 返回数组
return array
}
在删除节点的方法中我想到了三种方法:
1.直接将节点的所有HTML都设置为空,这样很直接的删除了所有节点
2.找到所有的子节点,然后同时遍历子节点然后一个个remove删除掉,但是当实践的时候发现当remove后再进行遍历,所有子节点的长度会实时发生变化,所以这个方法是不合理的.
3.由于第二种方法的子节点长度会实时变化,就想到了另外一种的遍历方式,设置一个变量,然后调用while循环,只要有子节点,就将这个第一个子节点删除,然后将删除后的节点中的第一个节点设置为这个变量,再进行循环,如以上代码所示
设置节点属性
attr(node, name, value) { // 重载(根据参数个数写不同的代码)
// 判断实参的长度,如果实参长度为3就设置
if(arguments.length === 3) {
node.setAttribute(name, value)
}else if(arguments.length === 2){
// 判断实参的长度,如果实参长度为2就读取
return node.getAttribute(name)
}
}
这是一个用来设置节点的方法,在考虑的时候我们有考虑到传入实参的个数,同时考虑到用户实际想获取还是想设置的同时调用不同的方法
设置节点的内容 会同时改掉该节点里面含有的子节点
text(node, string) { // 适配两种浏览器
if(arguments.length === 2){
// 判断如果有则用,没有就用另一种
if('innerText' in node) {
node.innerText = string //该方法是IE的比较通用
}else {
node.textContent = string // 该方法火狐谷歌等用的比较多
}
}else if(arguments.length ===1) {
if('innerText' in node) {
return node.innerText //该方法是IE的比较通用
}else {
return node.textContent// 该方法火狐谷歌等用的比较多
}
}
}
此为设置节点内容,同样用到了重载的方法,判断用户是想读取还是想设置,并且根据个别浏览器的支持采用了不同的DOM操作以便进行适配
改变内容的HTML
html(node, string) {
if(arguments.length === 2){
// 判断如果有则用,没有就用另一种
node.innerHTML = string
}else if(arguments.length ===1) {
return node.innerHTML
}
},
修改节点的style属性
style(node, name, value) {
// 若长度为3 是直接设置style样式
if(arguments.length === 3) {
node.style[name] = value
// 当长度为2时还需要判断第二个值类型为对象(设置),还是字符串(读取)
}else if(arguments.length === 2) {
if(typeof name === 'string') {
return node.style[name]
}else if(name instanceof Object) {
// 读取到所有的key
const object = name
for(let key in object) {
// 将每个的key设置为对象的key值
node.style[key] = object[key]
}
}
}
},
当实参为3个时直接设置即可,但是当2个实参是要进行判断,第二个实参是字符串的话就是获取,但是若为对象,则应进行遍历然后设置对应的name值
查看移除设置class
class: {
// 添加
add (node, className) {
node.classList.add(className)
},
// 移除
remove (node, className) {
node.classList.remove(className)
},
// 查看有无
has (node, className) {
return node.classList.contains(className)
}
},
在has方法中返回的为true or false
添加和移除监听事件
// 添加监听事件
on(node, eventName, fn) {
node.addEventListener(eventName, fn)
},
// 移除监听事件
off(node, eventName, fn) {
node.removeEventListener(eventName, fn)
},
运用添加和删除监听的方法,传入函数
给选择器查对应的节点
find(selector, scope) {
// 返回的是一个数组
// 若存在一个返回就用返回内 如果没有就在文档中找
return (scope || document).querySelectorAll(selector)
}
要判断是在全局当中选择,还是有给指定范围,通过||操作符若有范围则直接取范围,若没有则在全局中选择
找节点的父亲
parent(node) {
return node.parentNode
}
找节点的儿子
children(node) {
return node.children
}
用于获取兄弟姐妹,除了自己
siblings(node) {
// 将生成的伪数组转换成数组,再filter过滤
return Array.from(node.parentNode.children).filter(x => {
return x !== node
})
}
在之前的文章中有提到过filter函数,可以用来过滤符合与否我们的需求,但是必须为数组才能调用,我们获取到该节点的父亲的子节点后,然后判断只要不是当前节点就返回到一个数组中.
找到下一个节点
next(node) {
let x = node.nextSibling
// 判断是否下一个是文本
while(x && x.nodeType === 3) {
x = x.nextSibling
// 直到x是节点退出循环
}
return x
}
在这个找节点的方法中,我们想找到的是节点,但有时会返回一些空格的文本节点,不符合我们的要求,通过msdn中我查到node.nextSibling有返回值,若返回值为3时则为文本节点的方法,然后进行循环判断,只要不是文本节点则退出循环
找到上一个节点
prev(node) {
let x = node.previousSibling
// 判断是否下一个是文本
while(x && x.nodeType === 3) {
x = x.previousSibling
// 直到x是节点退出循环
}
return x
}
和找到下一个节点有共同的方法,同样需要进行循环判断 遍历所有节点
each(nodeList,fn) {
for(let i=0;i<nodeList.length;i++) {
fn.call(null, nodeList[i])
}
}
对所有节点进行遍历的同时,传入第二个函数,将函数的指向设置为空即可,可以同时设置节点的style
找到自己排第几
index(node) {
// 获取所有孩子,判断什么时候是自己 然后停止,返回
const list = dom.children(node.parentNode);
let i
for(i =0;i<list.length;i++) {
if(list[i] === node) {
break
}
}
return i
}
用children方法找到节点的父节点的所有孩子,然后进行遍历,若和当前节点相同则停止,返回i,即可找到自己是第几.
总结
这是最近在学习js中学到了很重要的一个思路,要学会使用循环,大家可以看到上面有很多地方都运用到了循环的方法,同时还要学会对文档的查询,这是一种找到问题,解决问题的关键所在,今天的文章就分享到这里了,谢谢阅读.
记得持续学习,不断跟进!加油!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!