美文网首页
jQuery的遍历结构设计之遍历祖先

jQuery的遍历结构设计之遍历祖先

作者: 小进进不将就 | 来源:发表于2019-03-22 09:46 被阅读0次

    前言:可以先自己动手做 ,然后回头看 一、二


    一、遍历归类
    遍历的接口可以归为几大类:
    (1)祖先
    (2)同胞兄弟
    (3)后代
    (4)过滤

    比如说 祖先的遍历接口:
    有从目标节点向上遍历单个父节点的 parent();
    有从目标节点向上遍历所有祖先节点的 parents();
    有从目标节点向上遍历到另一目标节点的 parentsUntil()

    这些遍历的接口不可能去一一实现,而是功能类似的接口,用统一的方法去封装。

    • 比如针对层级关系的处理,jQuery是用 dir 方法实现的,如下所示:
      // 针对层级关系的处理,jQuery就抽出了一个dir的方法,用于根据传递的元素与词素的位置关系,查找指定的元素。
      // parent,parents,parentsUntil等方法如代码所示:
    
      // parents:dir(elem,"parentNode")
      //parentsUntil:dir(elem, "parentNode", until)
      function dir(elem, dir, until) {
        let matched = []
        //parents:false
        let truncate = until !== undefined;
        //elem默认值为elem[dir]
        //举例:
        //let a=<li class="item-1">1</li>
        //a.parentNode=<ul class="level-2">xxx</ul>
    
        //9是Document,即整个文档的根节点
        //即递归的终结条件是到document结束
        while ((elem = elem[dir]) && elem.nodeType !== 9) {
          //1 Element即一个元素
          //意思就是elem是一个Element
          if (elem.nodeType === 1) {
            //parents方法不走这边
            //parentsUntil方法走这边
            //body
            if (truncate) {
              //在向上递归返回父节点的同时,判断是否达到条件
              //节点名和类名
              if (elem.nodeName.toLowerCase() === until || elem.className === until) {
                break;
              }
            }
            matched.push(elem);
          }
        }
    
        //写法二
        //elem由选择器判断,是必存在才会运行到此处的
        // while (elem.nodeType !== 9) {
        //   elem = elem[dir]
        //   xxx
        //   xxx
        // }
    
        return matched;
      }
    

    二、迭代器
    迭代器可以理解为一个遍历方法,
    该方法的第一个参数是一个 object,该对象的每个属性都是一个 function;
    该方法的第二个参数是一个 callback,该回调函数能够按顺序处理 object 的每个 function,并且不暴露 object 的内部。

    这也是设计模式中的迭代器模式。

    • jQuery的迭代器除了遍历外,还会将相同功能的代码合并处理:
    let ajQuery = {};
      //循环自定义一个对象,对象的每个属性的value是function,并在回调中对key、value进行操作
      //jQuery.each即$.each
      //$.each(object,callback(key,value))
      //$.each(array,callback(index,value))
      //详情请参考:http://api.jquery.com/jquery.each/
      jQuery.each({
        //本质即 elem.parentNode
        parent: function(elem) {
          //调用原生js的parentNode
          var parent = elem.parentNode;
          console.log(elem,'elem87')
          //11表示documentFragment
          //documentFragment是没有父节点parentNode的!!
          //详情请看:深入理解DOM节点类型第四篇——文档片段节点DocumentFragment
          // https://www.cnblogs.com/xiaohuochai/p/5816048.html
          return parent && parent.nodeType !== 11 ? parent : null;
        },
    
        //parents的本质即利用 elem.parentNode 向上递归直到document节点
        parents: function(elem) {
          return dir(elem, "parentNode");
        },
        //该方法从父元素向上遍历 DOM 元素的祖先,直至文档根元素的所有路径,直到到达指定的元素为止
        //until既可以是节点名也可以是类名
        parentsUntil: function(elem, until) {
          return dir(elem, "parentNode", until);
        }
      },
        //回调函数
        //对象:key value
        //数组:index value
        function(key, value) {
        console.log(key,value,'name107')
        //将jQuery的方法定义到ajQuery上
        ajQuery[key] = function(elem, until) {
          console.log(elem,until,'selector116')
          return  value(elem, until);
        };
      });
    

    三、轮到你了
    关键:

    //element表示原生DOM节点
    $().parent 本质即 elem.parentNode
    $().parents 的本质即利用 elem.parentNode 向上递归直到 document 节点
    $(). parentsUntil 的本质即 elem.nodeName.toLowerCase() === 另一目标节点名 || elem.className === 另一目标节点类名

    完整示例代码:

    <!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>
            </ul>
          </li>
          <li class="item-c">C</li>
        </ul>
      </li>
      <li class="item-iii">III</li>
    </ul>
    
    
    <script>
      // 针对层级关系的处理,jQuery就抽出了一个dir的方法,用于根据传递的元素与词素的位置关系,查找指定的元素。
      // parent,parents,parentsUntil等方法如代码所示:
    
      // parents:dir(elem,"parentNode")
      //parentsUntil:dir(elem, "parentNode", until)
      function dir(elem, dir, until) {
        let matched = []
        //parents:false
        let truncate = until !== undefined;
        //elem默认值为elem[dir]
        //举例:
        //let a=<li class="item-1">1</li>
        //a.parentNode=<ul class="level-2">xxx</ul>
    
        //9是Document,即整个文档的根节点
        //即递归的终结条件是到document结束
        while ((elem = elem[dir]) && elem.nodeType !== 9) {
          //1 Element即一个元素
          //意思就是elem是一个Element
          if (elem.nodeType === 1) {
            //parents方法不走这边
            //parentsUntil方法走这边
            //body
            if (truncate) {
              //在向上递归返回父节点的同时,判断是否达到条件
              //节点名和类名
              if (elem.nodeName.toLowerCase() === until || elem.className === until) {
                break;
              }
            }
            matched.push(elem);
          }
        }
    
        //写法二
        //elem由选择器判断,是必存在才会运行到此处的
        // while (elem.nodeType !== 9) {
        //   elem = elem[dir]
        //   xxx
        //   xxx
        // }
    
        return matched;
      }
    
      let ajQuery = {};
      //循环自定义一个对象,对象的每个属性的value是function,并在回调中对key、value进行操作
      //jQuery.each即$.each
      //$.each(object,callback(key,value))
      //$.each(array,callback(index,value))
      //详情请参考:http://api.jquery.com/jquery.each/
      jQuery.each({
        //本质即 elem.parentNode
        parent: function(elem) {
          //调用原生js的parentNode
          var parent = elem.parentNode;
          console.log(elem,'elem87')
          //11表示documentFragment
          //documentFragment是没有父节点parentNode的!!
          //详情请看:深入理解DOM节点类型第四篇——文档片段节点DocumentFragment
          // https://www.cnblogs.com/xiaohuochai/p/5816048.html
          return parent && parent.nodeType !== 11 ? parent : null;
        },
    
        //parents的本质即利用 elem.parentNode 向上递归直到document节点
        parents: function(elem) {
          return dir(elem, "parentNode");
        },
        //该方法从父元素向上遍历 DOM 元素的祖先,直至文档根元素的所有路径,直到到达指定的元素为止
        //until既可以是节点名也可以是类名
        parentsUntil: function(elem, until) {
          return dir(elem, "parentNode", until);
        }
      },
        //回调函数
        //对象:key value
        //数组:index value
        function(key, value) {
        console.log(key,value,'name107')
        //将jQuery的方法定义到ajQuery上
        ajQuery[key] = function(elem, until) {
          console.log(elem,until,'selector116')
          return  value(elem, until);
        };
      });
    
      $("#test1").click(function() {
        // <li class="item-1">1</li>
        var item = document.querySelectorAll('.item-1')[0]
        // console.log(item,'item93')
        //这他妈太神奇了。。
        console.log(item['parentNode'],'item94')
        console.log(item['className'],'item95')
        console.log(item.parentNode,'item101')
        console.log(item.parentNode.nodeName,'item117')
        console.log(item.parentNode.className,'item118')
        console.log(ajQuery.parent(item))
        console.log(ajQuery.parents(item).length)
        console.log(ajQuery.parentsUntil(item, 'body').length)
      })
      
      $("#test2").click(function() {
        var item = $('.item-1')
        console.log(item)
        console.log(item.parent()[0])
        console.log(item.parents().length)
        console.log(item.parentsUntil('body').length)
      })
    </script>
    </body>
    </html>
    
    

    githubhttps://github.com/AttackXiaoJinJin/jQueryExplain/blob/master/jQuery%E7%9A%84%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1%E4%B9%8B%E9%81%8D%E5%8E%86%E7%A5%96%E5%85%88.html


    (完)

    相关文章

      网友评论

          本文标题:jQuery的遍历结构设计之遍历祖先

          本文链接:https://www.haomeiwen.com/subject/dbgfvqtx.html