Поведение детей() и find() при использовании: сначала

Я немного смущен поведением при использовании children() и find() с :first.

Рассмотрим следующую разметку:

<div class="parent">
    <div>1</div>
    <div class="child">2</div>
    <div class="child">3</div>
</div>

<div class="parent">
    <div>1</div>
    <div class="child">2</div>
    <div class="child">3</div>
</div>

Из того, что я понимаю, следующее должно возвращать один и тот же набор элементов, так как нет внуков (это единственная разница между find() и children() в документация).

Однако, если я добавлю класс в первый .child, результаты будут разными:

$('.parent').find('.child:first').addClass('active');

Результаты следующей разметки:

<div class="parent">
    <div>1</div>
    <div class="child active">2</div>
    <div class="child">3</div>
</div>

<div class="parent">
    <div>1</div>
    <div class="child active">2</div>
    <div class="child">3</div>
</div>

Выполняя то же самое с помощью метода children(), я получаю:

<div class="parent">
    <div>1</div>
    <div class="child active">2</div>
    <div class="child">3</div>
</div>

<div class="parent">
    <div>1</div>
    <div class="child">2</div>
    <div class="child">3</div>
</div>

Почему?

Здесь скрипка

Ответ 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"). Это, наконец, оставит не более одного результата.

Ответ 2

    The children() method returns all direct children of the selected element.
    The find() method returns descendant elements of the selected element.
please check below links for that

http://www.w3schools.com/jquery/traversing_find.asp
http://www.w3schools.com/jquery/traversing_children.asp

Ответ 3

Цитата из JQuery API

The .children() method differs from .find() in that .children() only travels a single level down the DOM tree while .find() can traverse down multiple levels to select descendant elements (grandchildren, etc.) as well.

Это объясняет, почему .children() кладет только активный класс на 1 из них, а .find() проходит через все