• 如何理解与简化jquery的closest函数


    在实现delegate方法中,有一个很重要的辅助函数叫closest,虽然现在它归类为遍历节点这个模块中。这个函数实现得非常复杂,洋洋洒洒近50行,完全不符合极限编程的规矩。

    	closest: function( selectors, context ) {
    		var ret = [], i, l, cur = this[0];
    		
    		// Array
    		if ( jQuery.isArray( selectors ) ) {//这分支的过滤逻辑基本与下面的一致
    			var match, selector,
    				matches = {},
    				level = 1;
    
    			if ( cur && selectors.length ) {
    				for ( i = 0, l = selectors.length; i < l; i++ ) {
    					selector = selectors[i];
    
    					if ( !matches[ selector ] ) {
    						matches[ selector ] = POS.test( selector ) ?
    							jQuery( selector, context || this.context ) :
    							selector;
    					}
    				}
    
    				while ( cur && cur.ownerDocument && cur !== context ) {
    					for ( selector in matches ) {
    						match = matches[ selector ];//这里频繁创建新的jQuery对象与使用is这样复杂的方法,我不觉得其高效到哪里去
    
    						if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
    							ret.push({ selector: selector, elem: cur, level: level });
    						}
    					}
    
    					cur = cur.parentNode;
    					level++;
    				}
    			}
    
    			return ret;
    		}
    
    		// String
    		var pos = POS.test( selectors ) || typeof selectors !== "string" ?
    				jQuery( selectors, context || this.context ) :
    				0;
    
    		for ( i = 0, l = this.length; i < l; i++ ) {
    			cur = this[i];
    
    			while ( cur ) {
    				if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
    					ret.push( cur );
    					break;
    
    				} else {
    					cur = cur.parentNode;
    					if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
    						break;
    					}
    				}
    			}
    		}
    
    		ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
    
    		return this.pushStack( ret, "closest", selectors );
    	},
    

    恰逢我也想造个轮子,便去研究它一翻,发现其第一个可以是字符串,元素节点或jQuery对象,还有一个可选参数,上下文。看源码前几句,发现有个分支是判断是否是Array,估计是供内部调用的优化代码,可以无视之。于是其方法简化为:

    closest: function( selectors, context ) {
        var ret = [], i, l, cur = this[0];
       
        // 如果字符串包含位置伪类或者是个元素节点,则封装为一个jQuery对象,否则为0(即false的简写,用于快速跳过分支)
        var pos = POS.test( selectors ) || typeof selectors !== "string" ?
            jQuery( selectors, context || this.context ) :
            0;
        //遍历原jQuery对象的节点
        for ( i = 0, l = this.length; i < l; i++ ) {
            cur = this[i];
    
            while ( cur ) {
                //如果是jQuery对象,则判定其是否包含当前节点,否则使用matchesSelector方法判定这个节点是否匹配给定的表达式selectors
                if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
                    //是则放入选择器中
                    ret.push( cur );
                    break;
    
                } else {
                    //  否则把当前节点变为其父节点
                    cur = cur.parentNode;
                    if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
                        break;
                    }
                }
            }
        }
        //如果大于1,进行唯一化操作
        ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
        //将节点集合重新包装成一个新jQuery对象返回
        return this.pushStack( ret, "closest", selectors );
    },
    

    由于本人很反感位置伪类,认为其违反选择器的法则之一(由关系选择器隔开的各选择器分组内部,它们的位置是随意的),因此有关位置伪类的逻辑我也去掉了。

    closest: function( selectors ) {
        var ret = [], i, l, cur = this[0];
        // 如果字符串包含位置伪类或者是个元素节点,则封装为一个jQuery对象,否则为0(即false的简写,用于快速跳过分支)
        var node =  selectors.nodeType ? selectors :false;
        var nodes = [].slice.call(this);//将jQuery对象转换为纯数组
        //遍历原jQuery对象的节点
        for ( i = 0, l = this.length; i < l; i++ ) {
            cur = this[i];
    
            while ( cur ) {
                //如果是jQuery对象,则判定其是否包含当前节点,否则使用matchesSelector方法判定这个节点是否匹配给定的表达式selectors
                if ( obj ? nodes.indexOf(node) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
                    //indexOf方法在某些浏览器需要自行实现
                    //是则放入选择器中
                    ret.push( cur );
                    break;
                } else {
                    //  否则把当前节点变为其父节点
                    cur = cur.parentNode;
                    //如果没有父节点(说明其还没有插入DOM树),或不是元素节点,或是文档碎片(说明其刚被移出DOM树)
                    if ( !cur || !cur.ownerDocument || cur.nodeType === 11 ) {
                        break;
                    }
                }
            }
        }
        //如果大于1,进行唯一化操作
        ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
        //将节点集合重新包装成一个新jQuery对象返回
        return $(ret);//本人觉得pushStack真是个邪恶的方法,让菜鸟不籽有链下去的欲望,殊不知这是维护的大敌
    },
    

    注意,jquery1.6中closest方法不再是返回包含一个或零个节点的jQuery对象了,再是对应多个了(因此jQuery官网文档是错误的,没有即时同步这变化.)

    <!doctype html>
    <html>
      <head>
        <title>closest在jquery1.6的改变 by 司徒正美</title>
        <script src="jquery.js"></script>
        <script>
          $(function(){
            $("p").delegate("strong","click",function(){
              alert(this.innerHTML)
            });
            alert($("strong").closest("p").length)
          });
    
        </script>
    
      </head>
      <body>
        <p>
          <strong>使用事件代理1</strong>
        </p>
        <p>
          <strong>使用事件代理2</strong>
        </p>
        <p>
          <strong>使用事件代理3</strong>
        </p>
      </body>
    </html>
    

    下面是我的实现:

                closest: function( expr ) {
                    // 如果字符串包含位置伪类或者是个元素节点,则封装为一个dom对象,否则为0(即false的简写,用于快速跳过分支)
                    var node =  expr.nodeType ? expr : 0, nodes = dom.slice(this);//将它转换为纯数组
                    //遍历原dom对象的节点
                    for (var i = 0, ret = [], cur; cur = this[i++];) {//由于肯定里面都是节点,因此可以使用这种循环
                        while (cur && cur.nodeType === 1 ) {
                            //如果是dom对象,则判定其是否包含当前节点,否则使用matchesSelector方法判定这个节点是否匹配给定的表达式expr
                            if ( node ? nodes.indexOf(node) > -1 : matchElement( cur, expr ) ){
                                //indexOf方法在某些浏览器需要自行实现
                                //是则放入选择器中
                                ret.push( cur );
                                break;
                            } else {
                                // 否则把当前节点变为其父节点
                                cur = cur.parentNode;
                            }
                        }
                    }
                    //如果大于1,进行唯一化操作
                    ret = ret.length > 1 ? dom.unique( ret ) : ret;
                    //将节点集合重新包装成一个新dom对象返回
                    return this.labor(ret);
                },
    
  • 相关阅读:
    洛谷[P1002]过河卒
    ACM-Teleportation
    ACM-Team Tic Tac Toe
    Data_Structure04-树
    Data_Structure03-栈和队列
    Data_Structure02-线性表
    Data_Structure01-绪论
    C语言第二次实验报告
    C语言第一次实验报告
    mysql
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2043854.html
Copyright © 2020-2023  润新知