Recherche de la méthode jQuery find (..) qui inclut le nœud actuel

134

La méthode de traversée jQuery find (..) n'inclut pas le nœud actuel - elle commence par les enfants du nœud actuel. Quelle est la meilleure façon d'appeler une opération de recherche qui inclut le nœud actuel dans son algorithme de correspondance? En parcourant les documents, rien ne me saute immédiatement aux yeux.

John K
la source

Réponses:

153

Pour jQuery 1.8 et plus, vous pouvez utiliser .addBack(). Il faut un sélecteur pour que vous n'ayez pas besoin de filtrer le résultat:

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

Avant jQuery 1.8, vous étiez bloqué .andSelf()(maintenant obsolète et supprimé) qui nécessitait alors un filtrage:

object.find('selector').andSelf().filter('selector')
Robert
la source
5
J'ai regroupé cela sous forme de plugin jquery.findIncludeSelf, enregistré auprès de bower. Voir github.com/ronen/jquery.findIncludeSelf
ronen
18
@ronen Un fichier supplémentaire pour quelque chose qui peut être fait en une seule ligne? Merci mais, non merci.
6
@ prc322 un fichier supplémentaire ne me dérange pas pour les déploiements qui regroupent tout le javascript dans un seul fichier de toute façon. En général, je préfère utiliser des méthodes de bibliothèque (testées) même pour des choses simples, plutôt que d'encombrer mon code avec des choses que je trouve plus difficiles à lire et qui introduisent plus de possibilités de bogues. Dans ce cas en particulier, à mon humble avis, la nécessité de spécifier 'selector'deux fois rend l'encapsulation particulièrement souhaitable. YMMV
ronen
Ok, donc je suis peut-être épais, mais vous n'avez pas à spécifier 'selector'deux fois (comme mentionné par @ronen), ne pourriez-vous pas simplement utiliser object.parent().find('selector')??? - cela étant dit, j'aime l'idée d'une librairie qui le fait pour vous.
Sam
8
@ circa1983 object.parent().find('selector')comprend les frères objectet sœurs et leurs descendants.
Robert
41

Vous ne pouvez pas faire cela directement, le plus proche auquel je puisse penser est d'utiliser .andSelf()et d'appeler .filter(), comme ceci:

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

Malheureusement, .andSelf()ne prend pas de sélecteur, ce qui serait pratique.

Nick Craver
la source
7
Vous avez même ajouté un commentaire sur la page jquery juste après avoir répondu à cette question api.jquery.com/andSelf/#comment-50124533 Maintenant, c'est la rigueur. Agréable! J'ai fait ma diligence raisonnable et j'ai «aimé» celui-là aussi.
John K
2
Le second serait incroyablement lent. Le premier est tout simplement lent.
Tgr
@Tgr - Je ne suis pas en désaccord, même si le premier ne devrait pas être cette émission à moins que vous ayez affaire à un très grand nombre d'éléments. Si vous n'avez pas besoin de chaîner, vous pouvez ignorer le re-filtrage de ces éléments à coup sûr.
Nick Craver
3
Fait intéressant, closest(..)inclut l'élément DOM actuel et le haut de l'arborescence alors que toutes les méthodes de traversée vers le bas de l'arbre comme find(..)etc ne correspondent pas à l'élément actuel. C'est comme si l'équipe jQuery les implémentait délibérément sans se chevaucher lorsque les deux opérations étaient utilisées ensemble pour une recherche verticale complète.
John K
17
Gardez à l'esprit que .andSelf () est obsolète depuis la v1.8 et remplacé par .addBack () qui prend un sélecteur comme argument. Voir api.jquery.com/addBack
kkara
9

Définir

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

puis utilisez

$.findSelf(selector);

au lieu de

$find(selector);

Malheureusement, jQuery n'a pas cette fonction intégrée. Vraiment étrange pour tant d'années de développement. Mes gestionnaires AJAX n'étaient pas appliqués à certains éléments principaux en raison du fonctionnement de .find ().

Dmitriy Sintsov
la source
C'est buggy. Il ajoute tout "soi", même si un seul d'entre eux correspond ... - Une des raisons de ce bogue est une méthode jQuery au nom fou ...
Robert Siemer
J'ai essayé de corriger le bogue que vous avez signalé. Cela fonctionne-t-il correctement maintenant?
Dmitriy Sintsov
La plupart des autres réponses l'utilisent filter(), ce qui est plus logique.
Robert Siemer
5
$('selector').find('otherSelector').add($('selector').filter('otherSelector'))

Vous pouvez stocker $('selector')dans une variable pour l'accélération. Vous pouvez même écrire une fonction personnalisée pour cela si vous en avez beaucoup besoin:

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

$('selector').andFind('otherSelector')
Tgr
la source
Cela ne fonctionne que si vous commencez avec un sélecteur, ce qui peut ne pas être le cas. En outre, ce serait incorrect, ce $('selector').find('otherSelector').add($('otherSelector'))que vous avez maintenant équivaut à .andSelf(). Enfin, .andFind()ne filtre pas en fonction de l'expression, vous auriez besoin de .add($(this).filter(expr)):)
Nick Craver
@Nick Craver: ouais, j'ai oublié la partie filtrage, corrigé maintenant. Peu importe si $('selector')est remplacé par une autre méthode pour obtenir un objet jQuery (si c'est ce que vous vouliez dire en ne commençant pas par un sélecteur), il add()peut gérer tout comme le $()peut.
Tgr
Mon point était que vous $('selector')êtes peut-être $('selector').children('filter').closest('.class').last()... il peut être dans une chaîne et vous n'avez aucune idée de ce qu'est cet objet que vous ajoutez, donc la solution générique devrait prendre l'objet précédent comme le filtre :)
Nick Craver
Je ne vois toujours pas pourquoi ce serait un problème. thisest n'importe quel objet jQuery sur lequel un plugin a été appelé. Cela pourrait tout aussi bien être le résultat d'une chaîne d'appels.
Tgr
5

La réponse acceptée est très inefficace et filtre l'ensemble des éléments qui correspondent déjà.

//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
la source
3

Si vous souhaitez que le chaînage fonctionne correctement, utilisez l'extrait ci-dessous.

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

Après l'appel de la fonction end, elle renvoie l'élément #foo.

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

Sans définir de plugins supplémentaires, vous êtes coincé avec cela.

$('#foo')
    .find('.red')
        .addBack('.red')
            .css('color', 'red')
        .end()
    .end()
    .removeAttr('id');
SeregPie
la source
Ah, non ... Si thisplus d'un élément this.is()est déjà satisfait si un seul d'entre eux correspond.
Robert Siemer le
3

Dans le cas où vous recherchez exactement un élément , soit l'élément actuel, soit un à l'intérieur, vous pouvez utiliser:

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

Si vous recherchez plusieurs éléments, vous pouvez utiliser:

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

L'utilisation de andSelf/ andBackest assez rare, je ne sais pas pourquoi. Peut-être à cause des problèmes de performances que certains gars ont mentionnés avant moi.

(J'ai maintenant remarqué que Tgr a déjà donné cette deuxième solution)

oriadam
la source
2

Je sais que c'est une vieille question, mais il existe une manière plus correcte. Si l'ordre est important, par exemple lorsque vous correspondez à un sélecteur comme :first, j'ai écrit une petite fonction qui retournera exactement le même résultat que si elle find()incluait réellement l'ensemble actuel d'éléments:

$.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);
};

Cela ne sera en aucun cas efficace, mais c'est le meilleur que j'ai trouvé pour maintenir un bon ordre.

Justin Warkentin
la source
1

Je pense que andSelfc'est ce que tu veux:

obj.find(selector).andSelf()

Notez que cela rajoutera toujours le nœud actuel, qu'il corresponde ou non au sélecteur.

interjay
la source
1

Si vous regardez strictement dans le ou les nœuds actuels, vous faites simplement

$(html).filter('selector')
mikewasmike
la source
0

J'essayais de trouver une solution qui ne se répète pas (c'est -dire ne pas entrer deux fois dans le même sélecteur).

Et cette petite extension jQuery le fait:

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

Il combine find()(uniquement les descendants) avec filter()(uniquement l'ensemble actuel) et prend en charge tous les arguments consommés. Le pushStack()permet .end()de fonctionner comme prévu.

Utilisez comme ceci:

$(element).findWithSelf('.target')
Robert Siemer
la source
-2

Voici la vérité juste (mais triste):

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

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

Serge
la source
ne fonctionne pas avec les nœuds détachés (et le document lui-même), cependant.
John Dvorak
2
Euh, non, cela se supprimera $(selector)de l'ensemble dans tous les cas.
John Dvorak