jQuery的遍歷結構設計之遍歷同胞

進擊的小進進發表於2019-04-02

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的遍歷結構設計之遍歷同胞

(完)

相關文章