深入學習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);
}
});