前言:光看不行,需要动手操作!
一、本文实现的 jQuery 的同胞接口及作用如下
接口:$(selector).nextAll() 作用:向后遍历同胞,返回 selector 之后的所有同级元素
接口:$(selector).prevAll() 作用:向前遍历同胞,返回 selector 之前的所有同级元素
接口:$(selector).nextUntil(otherSelector) 作用:向后遍历同胞,返回在 selector 和 otherSelector 之间的所有同级元素
接口:$(selector).prevUntil(otherSelector) 作用:向前遍历同胞,返回在 otherSelector 和 selector 之间的所有同级元素
接口:$(selector).next() 作用:返回 selector 的后一个同级元素
接口:$(selector).prev() 作用:返回 selector 的前一个同级元素
接口:$(selector).siblings() 作用:返回 selector 的所有同胞元素(共享一个父元素,且不包括自己)
将这些接口封装在 jQuery 的迭代器中 ↓
二、迭代器 迭代器可以理解为一个遍历方法,该方法的第一个参数是一个 object,该对象的每个属性都是一个 function;该方法的第二个参数是一个 callback,该回调函数能够按顺序处理 object 的每个 function,并且不暴露 object 的内部。
jQuery 遍历同胞的迭代器代码:
let ajQuery = {}
jQuery.each({
// nextAll() 方法返回被选元素之后的所有同级元素
// 同级元素是共享相同父元素的元素
// 详细请看:http://www.runoob.com/jquery/traversing-nextall.html
//nextAll 的本质即利用 elem.nextSibling,抓取 element 节点,向后递归直到 elem=null
nextAll:function (elem) {
return dir(elem, “nextSibling”)
},
//prevAll() 方法返回被选元素之前的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-prevall.html
//prevAll 的本质即利用 elem.previousSibling,抓取 element 节点,向前递归直到 elem=null
prevAll:function (elem) {
return dir(elem, “previousSibling”)
},
// 返回在类名为 “item-1” 和 “item-4” 的两个 <li> 元素之间的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-nextuntil.html
//nextUntil 的本质即利用 elem.nextSibling,抓取 element 节点,
// 向后递归直到 elem.nodeName.toLowerCase()===until||elem.className === until
nextUntil:function (elem, until) {
return dir(elem, “nextSibling”, until)
},
// 返回在类名为 “item-4” 和 “item-1” 的两个 <li> 元素之间的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-prevuntil.html
//prevUntil 的本质即利用 elem.previousSibling,抓取 element 节点,
// 向前递归直到 elem.nodeName.toLowerCase()===until||elem.className === until
prevUntil: function (elem, until) {
return dir(elem, “previousSibling”, until)
},
//next: 返回被选元素的 后一个同级元素
next: function (elem) {
return sibling(elem, “nextSibling”);
},
//prev: 返回被选元素的 前一个同级元素
prev: function (elem) {
return sibling(elem, “previousSibling”);
},
//siblings: 返回被选元素的 所有同胞元素(共享一个父元素,且不包括自己)
siblings: function (elem) {
return dir(elem, “siblings”)
}
},
function(key, value) {
ajQuery[key] = function(elem, until) {
return value(elem, until);
}
})
三、使用 dir() 和 sibling() 方法来封装 遍历同胞 的方法
dir() 封装除 $().next() 和 $().pre() 以外的 遍历同胞的方法:
function dir(elem, dir, until) {
let matched = []
// 是否有另一目标节点
let hasUntil = until !== undefined
let sibElem
let isSibling=false
//$().siblings() 的实现有两种方法
// 第一种是先 prevAll 向前遍历目标元素的同胞元素,再 nextAll 向后遍历目标元素的同胞元素
// 第二种是先找到父元素的第一个元素,再 nextAll 向后遍历同胞元素,最后将 目标元素从数组中去掉
// 这里采用的是第二种方式
if(dir===’siblings’){
sibElem=elem
isSibling=true
elem=elem.parentNode.firstElementChild
dir=’nextSibling’
matched.push(elem)
}
//nextAll: 一直 nextSibling,直到 elem=null,退出循环
while ((elem = elem[dir])&&elem.nodeType !== 9) {
if (elem.nodeType === 1) {
//nextUntil:true
if (hasUntil) {
console.log(elem.nodeName.toLowerCase(),elem.className,’className44′)
if (elem.nodeName.toLowerCase() === until || elem.className === until) {
console.log(until,’until46′)
break
}
}
console.log(elem,’elem50′)
matched.push(elem)
}
}
// 循环完后,去除目标元素
if(isSibling){
console.log(sibElem,matched.indexOf(sibElem),’sibElem66′)
matched.splice(matched.indexOf(sibElem),1)
}
return matched
}
sibling() 封装 $().next() 和 $().pre() 方法:
function sibling(elem, dir) {
// 过滤掉不是 element 类型的元素
while ((elem = elem[dir]) && elem.nodeType !== 1) {}
return elem
}
四、完整代码及实例
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<title>jQuery 的遍历结构设计之遍历同胞 </title>
</head>
<body>
<script src=”jQuery.js”></script>
<button id=”test1″> 模拟遍历同胞 </button>
<button id=”test2″>jQuery 遍历同胞 </button>
<ul class=”level-1″>
<li class=”item-i”>I</li>
<!– 目标节点 –>
<li class=”item-ii”>II
<ul class=”level-2″>
<li class=”item-a”>A</li>
<li class=”item-b”>B
<ul class=”level-3″>
<li class=”item-1″>1</li>
<li class=”item-2″>2</li>
<li class=”item-3″>3</li>
<li class=”item-4″>4</li>
</ul>
</li>
<li class=”item-c”>C</li>
</ul>
</li>
<li class=”item-iii”>III</li>
</ul>
<script type=”text/javascript”>
function dir(elem, dir, until) {
let matched = []
let truncate = until !== undefined
let sibElem
let isSibling=false
// 遍历同胞元素有两种方法
// 第一种是先 prevAll 向前遍历目标元素的同胞元素,再 nextAll 向后遍历目标元素的同胞元素
// 第二种是先找到父元素的第一个元素,再 nextAll 向后遍历同胞元素,最后将 目标元素从数组中去掉
// 这里采用的是第二种方式
if(dir===’siblings’){
sibElem=elem
isSibling=true
elem=elem.parentNode.firstElementChild
dir=’nextSibling’
matched.push(elem)
}
//nextAll: 一直 nextSibling,直到 elem=null,退出循环
while ((elem = elem[dir])&&elem.nodeType !== 9) {
if (elem.nodeType === 1) {
//nextUntil:true
if (truncate) {
console.log(elem.nodeName.toLowerCase(),elem.className,’className44′)
if (elem.nodeName.toLowerCase() === until || elem.className === until) {
console.log(until,’until46′)
break
}
}
console.log(elem,’elem50′)
matched.push(elem)
}
}
// 循环完后,去除目标元素
if(isSibling){
console.log(sibElem,matched.indexOf(sibElem),’sibElem66′)
matched.splice(matched.indexOf(sibElem),1)
}
return matched
}
function sibling(elem, dir) {
// 过滤掉不是 element 类型的元素
while ((elem = elem[dir]) && elem.nodeType !== 1) {}
return elem
}
let ajQuery = {}
jQuery.each({
// nextAll() 方法返回被选元素之后的所有同级元素
// 同级元素是共享相同父元素的元素
// 详细请看:http://www.runoob.com/jquery/traversing-nextall.html
//nextAll 的本质即利用 elem.nextSibling,抓取 element 节点,向后递归直到 elem=null
nextAll:function (elem) {
return dir(elem, “nextSibling”)
},
//prevAll() 方法返回被选元素之前的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-prevall.html
//prevAll 的本质即利用 elem.previousSibling,抓取 element 节点,向前递归直到 elem=null
prevAll:function (elem) {
return dir(elem, “previousSibling”)
},
// 返回在类名为 “item-1” 和 “item-4” 的两个 <li> 元素之间的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-nextuntil.html
//nextUntil 的本质即利用 elem.nextSibling,抓取 element 节点,
// 向后递归直到 elem.nodeName.toLowerCase()===until||elem.className === until
nextUntil:function (elem, until) {
return dir(elem, “nextSibling”, until)
},
// 返回在类名为 “item-4” 和 “item-1” 的两个 <li> 元素之间的所有同级元素
// 详细请看:http://www.runoob.com/jquery/traversing-prevuntil.html
//prevUntil 的本质即利用 elem.previousSibling,抓取 element 节点,
// 向前递归直到 elem.nodeName.toLowerCase()===until||elem.className === until
prevUntil: function (elem, until) {
return dir(elem, “previousSibling”, until)
},
//next: 返回被选元素的 后一个同级元素
next: function (elem) {
return sibling(elem, “nextSibling”);
},
//prev: 返回被选元素的 前一个同级元素
prev: function (elem) {
return sibling(elem, “previousSibling”);
},
//siblings: 返回被选元素的 所有同胞元素(共享一个父元素,且不包括自己)
siblings: function (elem) {
return dir(elem, “siblings”)
}
},
function(key, value) {
ajQuery[key] = function(elem, until) {
return value(elem, until);
}
})
$(“#test1”).click(function(){
// let item = document.querySelectorAll(‘li.item-ii’)[0]
// let item = document.querySelectorAll(‘li.item-1’)[0]
let item = document.querySelectorAll(‘li.item-2’)[0]
//#text 3
//nodeType=3,而不是 1,所以不符合要求
// console.log(item.nextSibling,item.nextSibling.nodeType)
// console.log(item.nextSibling.nextSibling,item.nextSibling.nextSibling.nodeType)
// console.log(item.nextSibling.nextSibling.nextSibling,item.nextSibling.nextSibling.nextSibling.nodeType)
// //null
// console.log(item.nextSibling.nextSibling.nextSibling.nextSibling)
// console.log(ajQuery.nextAll(item))
// console.log(ajQuery.nextAll(item)[0].className)
// console.log(ajQuery.prevAll(item)[0].className)
// console.log(ajQuery.nextUntil(item, ‘item-4′),’item107’)
// console.log(ajQuery.nextUntil(item, ‘item-4′)[0].className,’item108’)
// console.log(ajQuery.prevUntil(item, ‘item-2′)[0].className)
//
// console.log(prev(item))
// console.log(next(item))
// Siblings item.parentNode.firstElementChild
console.log(ajQuery.siblings(item,’siblings’),’siblings151′)
})
$(“#test2”).click(function(){
let $item = $(‘li.item-2’)
console.log($item.nextAll()[0].className)
console.log($item.prevAll()[0].className)
console.log($item.nextUntil(‘item-4’))
console.log($item.nextUntil(‘item-4’)[0].className)
console.log($item.prevUntil(‘item-2’)[0].className)
console.log($item.prev())
console.log($item.next())
})
</script>
</body>
</html>
五、总结 注:element 表示 jQuery 对象 selector 的原生 DOM 节点
接口:$(selector).nextAll() 本质:利用 element=element.nextSibling,抓取 element 的同胞节点,并向后递归直到 element=null,此时返回 selector 向后遍历的所有同胞节点
接口:$(selector).prevAll() 本质:利用 element=element.previousSibling,抓取 element 的同胞节点,并向前递归直到 element=null,此时返回 selector 向前遍历的所有同胞节点
接口:$(selector).nextUntil(otherSelector) 本质:利用 element=element.nextSibling,抓取 element 的同胞节点,并向后递归直到 elem.nodeName.toLowerCase()===otherSelector || elem.className === otherSelector,此时返回 selector 向后遍历到 otherSelector 之间的所有同胞节点
接口:$(selector).prevUntil(otherSelector) 本质:利用 element=element.previousSibling,抓取 element 的同胞节点,并向前递归直到 elem.nodeName.toLowerCase()===otherSelector || elem.className === otherSelector,此时返回 selector 向前遍历到 otherSelector 之间的所有同胞节点
接口:$(selector).next() 本质:利用 element=element.nextSibling 和 elem.nodeType !== 1,过滤并抓取 元素类型 的后一个同胞节点
接口:$(selector).prev() 本质:利用 element=element.previousSibling 和 elem.nodeType !== 1,过滤并抓取 元素类型 的前一个同胞节点
接口:$(selector).siblings() 本质:先找到父元素的第一个元素,再 nextAll() 向后遍历同胞元素,最后将 selector 从数组中去掉
(完)