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.

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')

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.


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 ().


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')

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);

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');

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)


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.


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.


1

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

$(html).filter('selector')

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')

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.