Szukam metody find (..) jQuery, która zawiera bieżący węzeł

140

Metoda przechodzenia jQuery find (..) nie obejmuje bieżącego węzła - zaczyna się od elementów podrzędnych bieżącego węzła. Jaki jest najlepszy sposób wywołania operacji wyszukiwania, która zawiera bieżący węzeł w algorytmie dopasowywania? Przeglądając dokumenty, nic od razu na mnie nie wyskakuje.

John K.
źródło

Odpowiedzi:

158

W przypadku jQuery 1.8 i nowszych możesz użyć .addBack(). Wymaga selektora, więc nie musisz filtrować wyniku:

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

Przed jQuery 1.8 utknąłeś z .andSelf()(teraz przestarzałe i usunięte), które następnie wymagało filtrowania:

object.find('selector').andSelf().filter('selector')
Robert
źródło
5
Dodałem to jako wtyczkę jquery.findIncludeSelf, zarejestrowaną w bower. Zobacz github.com/ronen/jquery.findIncludeSelf
ronen
18
@ronen Dodatkowy plik do czegoś, co można zrobić w jednej linii? Dzięki, ale nie.
6
@ prc322 dodatkowy plik nie przeszkadza mi we wdrożeniach, które i tak łączą cały javascript w jeden plik. Generalnie wolę używać (przetestowanych) metod bibliotecznych nawet do prostych rzeczy, zamiast zaśmiecać mój kod rzeczami, które uważam za trudniejsze do odczytania i które wprowadzają więcej możliwości błędów. W szczególności w tym przypadku IMHO konieczność 'selector'dwukrotnego określenia sprawia, że ​​hermetyzacja jest wyjątkowo pożądana. YMMV
ronen
Ok, więc mogę być gruby, ale nie musisz określać 'selector'dwa razy (jak wspomniał @ronen), czy nie mógłbyś po prostu użyć object.parent().find('selector')??? - Biorąc to pod uwagę, podoba mi się pomysł biblioteki, która robi to za Ciebie.
Sam
8
@ circa1983 object.parent().find('selector')obejmuje rodzeństwo objecti ich potomków.
Robert,
41

Nie możesz tego zrobić bezpośrednio, najbliższe, o czym przychodzi mi do głowy, to używanie .andSelf()i dzwonienie .filter(), na przykład:

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

Niestety .andSelf()nie bierze selektora, który by się przydał.

Nick Craver
źródło
7
Dodałeś nawet komentarz do strony jquery zaraz po udzieleniu odpowiedzi na to pytanie api.jquery.com/andSelf/#comment-50124533 Teraz to jest dokładność. Ładny! Zrobiłem swoją należytą staranność i to też mi się podobało.
John K
2
Drugi byłby oszałamiająco powolny. Pierwsza jest po prostu powolna.
Tgr
@Tgr - Nie zgadzam się, chociaż pierwszy nie powinien być tym pokazem, chyba że masz do czynienia z bardzo dużą liczbą elementów. Jeśli nie musisz łączyć w łańcuch, na pewno możesz pominąć ponowne filtrowanie tych elementów.
Nick Craver
3
Co ciekawe, closest(..)zawiera bieżący element DOM i wyżej w drzewie, podczas gdy wszystkie metody przechodzenia w dół drzewa, takie jak find(..)etc, nie pasują do bieżącego elementu. To tak, jakby zespół jQuery celowo zaimplementował je, aby nie nakładały się, gdy obie operacje są używane razem do pełnego wyszukiwania pionowego.
John K
17
Należy pamiętać, że funkcja .andSelf () została wycofana od wersji 1.8 i zastąpiona przez .addBack (), która przyjmuje selektor jako argument. Zobacz api.jquery.com/addBack
kkara,
9

Definiować

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

następnie użyj

$.findSelf(selector);

zamiast

$find(selector);

Niestety jQuery nie ma tego wbudowanego. Naprawdę dziwne przez tyle lat rozwoju. Moje programy obsługi AJAX nie zostały zastosowane do niektórych najważniejszych elementów z powodu działania .find ().

Dmitriy Sintsov
źródło
To jest buggy. Dodaje całe „siebie”, nawet jeśli tylko jeden z nich pasuje ... - Jednym z powodów tego błędu jest kiepsko nazwana metoda jQuery ...
Robert Siemer
Próbowałem naprawić zgłoszony przez Ciebie błąd. Czy teraz działa poprawnie?
Dmitriy Sintsov
Większość innych odpowiedzi filter()znajduje się tam, co ma większy sens.
Robert Siemer
5
$('selector').find('otherSelector').add($('selector').filter('otherSelector'))

Możesz zapisać $('selector')w zmiennej w celu przyspieszenia. Możesz nawet napisać niestandardową funkcję do tego, jeśli bardzo jej potrzebujesz:

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

$('selector').andFind('otherSelector')
Tgr
źródło
Działa to tylko wtedy, gdy zaczynasz od selektora, co może nie mieć miejsca. Ponadto, to jest niepoprawne, byłoby $('selector').find('otherSelector').add($('otherSelector'))to równoważne temu, co masz teraz .andSelf(). Wreszcie, .andFind()nie filtruje na podstawie wyrażenia, trzeba by .add($(this).filter(expr)):)
Nick Craver
@Nick Craver: tak, zapomniałem o części filtrującej, naprawionej teraz. Naprawdę nie ma znaczenia, czy $('selector')zostanie zastąpiona inną metodą uzyskiwania obiektu jQuery (jeśli o to chodziło, nie zaczynając od selektora), add()poradzi sobie ze wszystkim tak samo, jak $()może.
Tgr
Chodziło mi o to, że twój $('selector')$('selector').children('filter').closest('.class').last()Chodziło mi o to, może być ... może być w łańcuchu i nie masz pojęcia, jaki obiekt jest dodawany, więc ogólne rozwiązanie powinno przyjąć poprzedni obiekt tak, jak robi to filtr :)
Nick Craver
Nadal nie rozumiem, dlaczego miałby to być problem. thisjest jakimkolwiek obiektem jQuery, do którego została wywołana wtyczka. Równie dobrze może to być wynikiem łańcucha połączeń.
Tgr
5

Przyjęta odpowiedź jest bardzo nieefektywna i filtruje zestaw elementów, które są już dopasowane.

//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
źródło
3

Jeśli chcesz, aby łańcuch działał poprawnie, użyj poniższego fragmentu.

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

Po wywołaniu funkcji end zwraca element #foo.

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

Bez definiowania dodatkowych wtyczek utkniesz z tym.

$('#foo')
    .find('.red')
        .addBack('.red')
            .css('color', 'red')
        .end()
    .end()
    .removeAttr('id');
SeregPie
źródło
Ach, nie ... Jeśli thisjest więcej niż jeden element, this.is()jest już spełniony, jeśli tylko jeden z nich pasuje.
Robert Siemer
3

W przypadku, gdy szukasz dokładnie jednego elementu , bieżącego lub wewnątrz niego, możesz użyć:

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

Jeśli szukasz wielu elementów , możesz użyć:

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

Korzystanie z andSelf/andBack jest dość rzadkie, nie wiem dlaczego. Może z powodu problemów z wydajnością, o których wspominali niektórzy faceci.

(Teraz zauważyłem, że Tgr podał już to drugie rozwiązanie)

oriadam
źródło
2

Wiem, że to stare pytanie, ale jest bardziej poprawny sposób. Jeśli kolejność jest ważna, na przykład gdy dopasowujesz selektor, taki jak :first, napisałem małą funkcję, która zwróci dokładnie taki sam wynik, jak gdyby find()faktycznie zawierał bieżący zestaw elementów:

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

W żadnym wypadku nie będzie skuteczny, ale to najlepsze, co wymyśliłem, aby utrzymać porządek.

Justin Warkentin
źródło
1

Myślę, że andSelftego chcesz:

obj.find(selector).andSelf()

Zauważ, że to zawsze doda z powrotem bieżący węzeł, niezależnie od tego, czy pasuje do selektora, czy nie.

interjay
źródło
1

Jeśli dokładnie patrzysz na bieżący węzeł (węzły), po prostu to robisz

$(html).filter('selector')
mikewasmike
źródło
0

Próbowałem znaleźć rozwiązanie, które się nie powtarza (tj. Nie wchodząc dwukrotnie w ten sam selektor).

I to małe rozszerzenie jQuery to robi:

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

Łączy find()(tylko elementy potomne) z filter()(tylko bieżącym zestawem) i obsługuje wszystkie argumenty, które zjadają oba. pushStack()Pozwala na.end() do pracy zgodnie z oczekiwaniami.

Użyj w ten sposób:

$(element).findWithSelf('.target')
Robert Siemer
źródło
-2

Oto właściwa (ale smutna) prawda:

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

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

Serge
źródło
nie działa jednak z odłączonymi węzłami (i samym dokumentem).
John Dvorak
2
Uh, nie, to usunie $(selector)się z zestawu we wszystkich przypadkach.
John Dvorak