Ищем метод jQuery find (..), который включает текущий узел

134

Метод обхода jQuery find (..) не включает текущий узел - он начинается с дочерних элементов текущего узла. Как лучше всего вызвать операцию поиска, которая включает текущий узел в свой алгоритм сопоставления? Просматривая документы, у меня сразу ничего не выскакивает.

Джон К
источник

Ответы:

153

Для jQuery 1.8 и выше вы можете использовать .addBack(). Требуется селектор, поэтому вам не нужно фильтровать результат:

object.find('selector').addBack('selector')

До jQuery 1.8 вы застряли .andSelf()(теперь устарело и удалено), который затем нуждался в фильтрации:

object.find('selector').andSelf().filter('selector')
Роберт
источник
5
Я связал это как плагин jquery.findIncludeSelf, зарегистрированный в bower. См github.com/ronen/jquery.findIncludeSelf
Ронен
18
@ronen Дополнительный файл для чего-то, что можно сделать одной строкой? Спасибо, но нет.
6
@ prc322 дополнительный файл не беспокоит меня из-за развертываний, которые все равно объединяют весь javascript в один файл. Как правило, я предпочитаю использовать (протестированные) библиотечные методы даже для простых вещей, а не загромождать свой код вещами, которые мне труднее читать и что дает больше возможностей для ошибок. В частности, в этом случае, ИМХО, необходимость указывать 'selector'дважды делает инкапсуляцию более желательной. YMMV
ronen
Хорошо, я мог бы быть толстым, но вам не нужно указывать 'selector'дважды (как упоминалось @ronen), не могли бы вы просто использовать object.parent().find('selector')??? - при этом мне нравится идея библиотеки, которая сделает это за вас.
Сэм
8
@ circa1983 object.parent().find('selector')включает братьев objectи сестер и их потомков.
Роберт
41

Вы не можете сделать это напрямую, самое близкое, что я могу придумать, - это использовать .andSelf()и вызывать .filter(), например:

$(selector).find(oSelector).andSelf().filter(oSelector)
//or...
$(selector).find('*').andSelf().filter(oSelector);

К сожалению .andSelf(), селектор не нужен, что было бы удобно.

Ник Крейвер
источник
7
Вы даже добавили комментарий на страницу jquery сразу после ответа на этот вопрос api.jquery.com/andSelf/#comment-50124533 Вот и все. Ницца! Я проявил должную осмотрительность, и это тоже мне понравилось.
Джон К.
2
Второй будет невероятно медленным. Первый просто медленный.
Tgr
@Tgr - Я не возражаю, хотя первым не должно быть этого шоу, если вы не имеете дело с очень большим количеством элементов. Если вам не нужно создавать цепочки, вы можете точно пропустить повторную фильтрацию этих элементов.
Ник Крейвер
3
Интересно, что closest(..)включает текущий элемент DOM и вверх по дереву, тогда как все методы обхода вниз по дереву, такие как и find(..)т.д., не соответствуют текущему элементу. Это как если бы команда jQuery целенаправленно реализовала их для исключения дублирования, когда обе операции используются вместе для полного вертикального поиска.
Джон К.
17
Имейте в виду, что .andSelf () устарел с версии 1.8 и заменен на .addBack (), который принимает селектор в качестве аргумента. См. Api.jquery.com/addBack
kkara 08
9

определять

$.fn.findSelf = function(selector) {
    var result = this.find(selector);
    this.each(function() {
        if ($(this).is(selector)) {
            result.add($(this));
        }
    });
    return result;
};

затем используйте

$.findSelf(selector);

вместо того

$find(selector);

К сожалению, в jQuery этого нет. Действительно странно для стольких лет разработки. Мои обработчики AJAX не применялись к некоторым верхним элементам из-за того, как работает .find ().

Дмитрий Синцов
источник
Это глючит. Он добавляет все "self", даже если только одно из них соответствует ... - Одна из причин этой ошибки - метод jQuery с дурацким названием ...
Роберт Симер
Я пытался исправить ошибку, о которой вы сообщили. Теперь он работает нормально?
Дмитрий Синцов,
Большинство других ответов используют filter()там, что имеет больше смысла.
Роберт Симер
5
$('selector').find('otherSelector').add($('selector').filter('otherSelector'))

Вы можете сохранить $('selector')в переменной для ускорения. Вы даже можете написать для этого собственную функцию, если она вам очень нужна:

$.fn.andFind = function(expr) {
  return this.find(expr).add(this.filter(expr));
};

$('selector').andFind('otherSelector')
Tgr
источник
Это работает, только если вы начинаете с селектора, хотя это может быть не так. Кроме того, это неправильно, было бы то $('selector').find('otherSelector').add($('otherSelector')), что у вас сейчас эквивалентно .andSelf(). Наконец, .andFind()не фильтрует по выражению, вам нужно .add($(this).filter(expr)):)
Ник Крейвер
@Nick Craver: да, я забыл фильтрующую часть, теперь исправлено. На самом деле не имеет значения, $('selector')заменен ли он каким-либо другим методом получения объекта jQuery (если это то, что вы имели в виду, не начиная с селектора), add()может обрабатывать все, что и $()может.
Tgr
Я хотел сказать, что ваш $('selector')может быть $('selector').children('filter').closest('.class').last()... он может быть в цепочке, и вы не знаете, что это за объект, который вы добавляете, поэтому универсальное решение должно принимать предыдущий объект, как и фильтр :)
Ник Крейвер
Я до сих пор не понимаю, почему это может быть проблемой. thisэто какой-либо объект jQuery, к которому был вызван плагин. С таким же успехом это могло быть результатом цепочки вызовов.
Tgr
5

Принятый ответ очень неэффективен и фильтрует набор уже сопоставленных элементов.

//find descendants that match the selector
var $selection = $context.find(selector);
//filter the parent/context based on the selector and add it
$selection = $selection.add($context.filter(selector);
erikrestificar
источник
3

Если вы хотите, чтобы цепочка работала правильно, используйте приведенный ниже фрагмент.

$.fn.findBack = function(expr) {
    var r = this.find(expr);
    if (this.is(expr)) r = r.add(this);
    return this.pushStack(r);
};

После вызова функции end возвращает элемент #foo.

$('#foo')
    .findBack('.red')
        .css('color', 'red')
    .end()
    .removeAttr('id');

Без определения дополнительных плагинов вы застряли на этом.

$('#foo')
    .find('.red')
        .addBack('.red')
            .css('color', 'red')
        .end()
    .end()
    .removeAttr('id');
SeregPie
источник
Ах, нет ... Если thisбольше, чем один элемент this.is(), уже удовлетворен, если только один из них соответствует.
Роберт Симер,
3

Если вы ищете ровно один элемент , текущий или находящийся внутри него, вы можете использовать:

result = elem.is(selector) ? elem : elem.find(selector);

Если вам нужно несколько элементов, вы можете использовать:

result = elem.filter(selector).add(elem.find(selector));

Использование andSelf/ andBackдовольно редко, не знаю почему. Возможно, из-за проблем с производительностью, о которых до меня говорили некоторые ребята.

(Теперь я заметил, что Tgr уже дал это второе решение)

oriadam
источник
2

Я знаю, что это старый вопрос, но есть более правильный способ. Если порядок важен, например, когда вы сопоставляете такой селектор :first, я написал небольшую функцию, которая будет возвращать точно такой же результат, как если бы find()фактически включал текущий набор элементов:

$.fn.findAll = function(selector) {
  var $result = $();

  for(var i = 0; i < this.length; i++) {
    $result = $result.add(this.eq(i).filter(selector));
    $result = $result.add(this.eq(i).find(selector));
  }

  return $result.filter(selector);
};

Это ни в коем случае не будет эффективным, но это лучшее, что я придумал для поддержания надлежащего порядка.

Джастин Варкентин
источник
1

Я думаю andSelf, что вы хотите:

obj.find(selector).andSelf()

Обратите внимание, что при этом всегда будет добавляться текущий узел, независимо от того, соответствует он селектору или нет.

interjay
источник
1

Если вы строго смотрите в текущем узле (ах), вы просто делаете

$(html).filter('selector')
mikewasmike
источник
0

Я пытался найти решение, которое не повторяется (т.е. не вводит один и тот же селектор дважды).

И это крошечное расширение jQuery делает это:

jQuery.fn.findWithSelf = function(...args) {
  return this.pushStack(this.find(...args).add(this.filter(...args)));
};

Он объединяет find()(только потомков) с filter()(только текущий набор) и поддерживает любые аргументы, которые оба едят. Это pushStack()позволяет .end()работать должным образом.

Используйте как это:

$(element).findWithSelf('.target')
Роберт Симер
источник
-2

Вот правильная (но печальная) правда:

$(selector).parent().find(oSelector).filter($(selector).find('*'))

http://jsfiddle.net/SergeJcqmn/MeQb8/2/

саржа
источник
однако не работает с отдельными узлами (и самим документом).
Джон Дворжак
2
Эээ, нет, это удалится $(selector)само из набора во всех случаях.
Джон Дворжак