天天看點

深入學習jquery源碼之has()和closest()

深入學習jquery源碼之has()和closest()

has(expr|ele)

概述

保留包含特定後代的元素,去掉那些不含有指定後代的元素。

.has()方法将會從給定的jQuery對象中重新建立一組比對的對象。提供的選擇器會一一測試原先那些對象的後代,含有比對後代的對象将得以保留。

參數

expr String

一個選擇器字元串。

element DOMElement

一個DOM元素

給含有ul的li加上背景色

<ul>
  <li>list item 1</li>
  <li>list item 2
    <ul>
      <li>list item 2-a</li>
      <li>list item 2-b</li>
    </ul>
  </li>
  <li>list item 3</li>
  <li>list item 4</li>
</ul>
           
$('li').has('ul').css('background-color', 'red');
           

closest(expr,[context]|object|element)

概述

jQuery 1.3新增。從元素本身開始,逐級向上級元素比對,并傳回最先比對的元素。。

closest會首先檢查目前元素是否比對,如果比對則直接傳回元素本身。如果不比對則向上查找父元素,一層一層往上,直到找到比對選擇器的元素。如果什麼都沒找到則傳回一個空的jQuery對象。

closest和parents的主要差別是:1,前者從目前元素開始比對尋找,後者從父元素開始比對尋找;2,前者逐級向上查找,直到發現比對的元素後就停止了,後者一直向上查找直到根元素,然後把這些元素放進一個臨時集合中,再用給定的選擇器表達式去過濾;3,前者傳回0或1個元素,後者可能包含0個,1個,或者多個元素。

closest對于處理事件委托非常有用。

參數

expr String,Array

用以過濾元素的表達式。jQuery 1.4開始,也可以傳遞一個字元串數組,用于查找多個元素。

expr,[context] String

expr:用以過濾子元素的表達式

context:DOM元素在其中一個比對的元素可以被發現。如果沒有上下文在當時的情況下通過了jQuery設定将被使用。

jQuery object  object

一個用于比對元素的jQuery對象

element DOMElement

一個用于比對元素的DOM元素。

展示如何使用clostest查找多個元素

<ul><li></li><li></li></ul>
           
$("li:first").closest(["ul", "body"]);
           
[ul, body]
           

展示如何使用clostest來完成事件委托。

<ul>
    <li><b>Click me!</b></li>
    <li>You can also <b>Click me!</b></li>
</ul>
           
$(document).bind("click", function (e) {
    $(e.target).closest("li").toggleClass("hilight");
});
           

jquery源碼

jQuery = function (selector, context) {
            // The jQuery object is actually just the init constructor 'enhanced'
            // Need init if jQuery is called (just allow error to be thrown if not included)
            return new jQuery.fn.init(selector, context);
	}
	
	init = jQuery.fn.init = function (selector, context) {
	
	}
	
	    var rneedsContext = jQuery.expr.match.needsContext;
        var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
        var risSimple = /^.[^:#\[\.,]*$/;
		
	jQuery.filter = function (expr, elems, not) {
        var elem = elems[0];

        if (not) {
            expr = ":not(" + expr + ")";
        }

        return elems.length === 1 && elem.nodeType === 1 ?
            jQuery.find.matchesSelector(elem, expr) ? [elem] : [] :
            jQuery.find.matches(expr, jQuery.grep(elems, function (elem) {
                return elem.nodeType === 1;
            }));
    };
	
	  rnative = /^[^{]+\{\s*\[native \w/
	     hasCompare = rnative.test(docElem.compareDocumentPosition);

                // Element contains another
                // Purposefully does not implement inclusive descendent
                // As in, an element does not contain itself
         contains = hasCompare || rnative.test(docElem.contains) ?
                    function (a, b) {
                        var adown = a.nodeType === 9 ? a.documentElement : a,
                            bup = b && b.parentNode;
                        return a === bup || !!(bup && bup.nodeType === 1 && (
                            adown.contains ?
                                adown.contains(bup) :
                                a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
                        ));
                    } :
                    function (a, b) {
                        if (b) {
                            while ((b = b.parentNode)) {
                                if (b === a) {
                                    return true;
                                }
                            }
                        }
                        return false;
         };
	
	
	jQuery.fn.extend({
        has: function (target) {
            var i,
                targets = jQuery(target, this),
                len = targets.length;

            return this.filter(function () {
                for (i = 0; i < len; i++) {
                    if (jQuery.contains(this, targets[i])) {
                        return true;
                    }
                }
            });
        },

        closest: function (selectors, context) {
            var cur,
                i = 0,
                l = this.length,
                matched = [],
                pos = rneedsContext.test(selectors) || typeof selectors !== "string" ?
                    jQuery(selectors, context || this.context) :
                    0;

            for (; i < l; i++) {
                for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) {
                    // Always skip document fragments
                    if (cur.nodeType < 11 && (pos ?
                        pos.index(cur) > -1 :

                        // Don't pass non-elements to Sizzle
                        cur.nodeType === 1 &&
                        jQuery.find.matchesSelector(cur, selectors))) {

                        matched.push(cur);
                        break;
                    }
                }
            }

            return this.pushStack(matched.length > 1 ? jQuery.unique(matched) : matched);
        }
    });