jQuery的遍历结构设计之遍历同胞

jQuery的遍历结构设计之遍历同胞

前言:光看不行,需要动手操作!


一、本文实现的 jQuery 的同胞接口及作用如下

接口:$(selector).nextAll()
作用:向后遍历同胞,返回 selector的所有同级元素

接口:$(selector).prevAll()
作用:向前遍历同胞,返回 selector的所有同级元素

接口:$(selector).nextUntil(otherSelector)
作用: 向后遍历同胞,返回在 selectorotherSelector 之间的所有同级元素

接口:$(selector).prevUntil(otherSelector)
作用: 向前遍历同胞,返回在 otherSelectorselector 之间的所有同级元素

接口:$(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 从数组中去掉


jQuery的遍历结构设计之遍历同胞

(完)

相关推荐