深入学习jquery源码之has()和closest()
has(expr|ele)
概述
保留包含特定后代的元素,去掉那些不含有指定后代的元素。
.has()方法将会从给定的jQuery对象中重新创建一组匹配的对象。提供的选择器会一一测试原先那些对象的后代,含有匹配后代的对象将得以保留。
参数
expr String
一个选择器字符串。
element DOMElement
一个DOM元素
给含有ul的li加上背景色
- list item 1
- list item 2
- list item 2-a
- list item 2-b
- list item 3
- list item 4
$('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查找多个元素
$("li:first").closest(["ul", "body"]);
[ul, body]
展示如何使用clostest来完成事件委托。
- Click me!
- You can also Click me!
$(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 = (/^(?:|)$/);
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);
}
});