Ответ 1
Хороший вопрос!
TL; DR
Разница заключается в том, что find
выполняет индивидуальный поиск, начиная с каждого завернутого элемента, а затем агрегирует результаты, а children
получает единый совокупный пул результатов кандидата, который затем фильтруется на основе указанного селектора. Это приводит к выборам типа :first
, дающим другой результат, если исходный объект jQuery обертывает более одного элемента.
Причина этого понятна (children
знает из get-go, что все его возможные совпадения разделяют очень важное свойство в DOM, поэтому имеет смысл сузить список кандидатов вперед по соображениям производительности), но IMO вы не можете назвать результат чем-то другим, кроме ошибки в текущей реализации.
$.fn.find
find
создает два совпадения, так как он выполняет поиск по каждому из обернутых элементов текущего объекта jQuery и затем агрегирует результаты. Поэтому для каждого .parent
мы сопоставляем первого потомка в порядке документа, который является .child
.
Здесь источник:
function (selector) {
var i, ret = [],
self = this,
len = self.length;
if (typeof selector !== "string") {
return this.pushStack(jQuery(selector).filter(function () {
for (i = 0; i < len; i++) {
if (jQuery.contains(self[i], this)) {
return true;
}
}
}));
}
for (i = 0; i < len; i++) {
jQuery.find(selector, self[i], ret); // ** IMPORTANT **
}
// Needed because $( selector, context ) becomes $( context ).find(selector)
ret = this.pushStack(len > 1 ? jQuery.unique(ret) : ret);
ret.selector = this.selector ? this.selector + " " + selector : selector;
return ret;
}
Все происходит в строке с надписью "ВАЖНО": jQuery.find
является псевдонимом Sizzle
, который добавляет результаты к ret
каждый раз. Очевидно, что если вы делаете .find(":first")
в объекте jQuery, который обертывает N элементов, каждый из которых имеет по крайней мере один потомок, вы получите ровно N результатов.
$.fn.children
children
выполняет другой маршрут: для каждого обернутого элемента он перемещается по DOM, чтобы получить доступ к своим дочерним элементам, а затем фильтрует результаты в целом на основе селектора. Очевидно, что в этом случае это оставило бы не более одного элемента в качестве конечного результата.
Вот как это происходит:
function (until, selector) {
var ret = jQuery.map(this, fn, until); // ** IMPORTANT 1 **
if (name.slice(-5) !== "Until") {
selector = until;
}
if (selector && typeof selector === "string") {
ret = jQuery.filter(selector, ret); // ** IMPORTANT 2 **
}
if (this.length > 1) {
// Remove duplicates
if (!guaranteedUnique[name]) {
ret = jQuery.unique(ret);
}
// Reverse order for parents* and prev-derivatives
if (rparentsprev.test(name)) {
ret = ret.reverse();
}
}
return this.pushStack(ret);
}
Это не так понятно, потому что код делится на кучу методов, которые выполняют структурный обход DOM (parent
, next
, prev
, siblings
и т.д.), но снова соответствующая часть кода очень проста: строка "ВАЖНО 1" собирает результаты структурного обхода ( "get the children" ) внутри ret
, и эти результаты в целом фильтруются на основе селектора (".child:first"
). Это, наконец, оставит не более одного результата.