Znajdowanie najbliższego elementu bez jQuery


90

Próbuję znaleźć najbliższy element o określonej nazwie tagu bez jQuery. Po kliknięciu <th>ikony Chcę uzyskać dostęp do <tbody>tabeli dla tego stołu. Propozycje? Czytałem o offsetie, ale nie za bardzo go rozumiałem. Powinienem po prostu użyć:

Załóżmy, że th jest już ustawiony na kliknięty element

th.offsetParent.getElementsByTagName('tbody')[0]

1
Jeśli stwierdzisz, że musisz zacząć przechodzić przez DOM, to jest to jedna z instancji, w której dodatkowe kbs przypisane do jquery byłyby tego warte.
Kevin Bowersox,


2
Myślę, że to bardzo ważne i aktualne pytanie. Nie ma powodu do głosów przeciw.

el.closest('tbody')dla przeglądarek innych niż IE. Zobacz bardziej rozbudowaną odpowiedź + wypełnienie poniżej.
oriadam

Odpowiedzi:


69

Niewiele (bardzo) późno na imprezę, ale mimo to. To powinno załatwić sprawę :

function closest(el, selector) {
    var matchesFn;

    // find vendor prefix
    ['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
        if (typeof document.body[fn] == 'function') {
            matchesFn = fn;
            return true;
        }
        return false;
    })

    var parent;

    // traverse parents
    while (el) {
        parent = el.parentElement;
        if (parent && parent[matchesFn](selector)) {
            return parent;
        }
        el = parent;
    }

    return null;
}

2
Świetna odpowiedź, która działa wspaniale. MDN ma również wypełnienie dla element.closest (). Chrome zawiera element.matches () w bieżącej wersji, więc przedrostek nie jest potrzebny. Właśnie dodałem to do biblioteki, której używam w aplikacji, którą tworzę, Clibu.
nevf

2
elZmieniłem kod tak, aby testował również element, który wykonuje jQuery.closest (), a także Element.closest (). for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nevf

1
Ten kod jest w porządku, ale brakuje w nim średnika, a także definiuje go parentw zakresie globalnym (!)
Steven Lu

1
Warto wspomnieć: developer.mozilla.org/en-US/docs/Web/API/Element/closest natywna metoda dla najbliższych, choć mało obsługiwanych: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D

Tylko uwaga na ten temat, możesz chcieć to zrobić el.parentNodelub to się zepsuje podczas przechodzenia przez SVG w IE.
smcka

124

Bardzo prosta:

el.closest('tbody')

Obsługiwane we wszystkich przeglądarkach oprócz IE.
AKTUALIZACJA : Edge również ją obsługuje.

Nie ma potrzeby korzystania z jQuery. Więcej więcej, zastępując jQuery jest $(this).closest('tbody')z $(this.closest('tbody'))zwiększy wydajność, znacznie, gdy element nie zostanie znaleziony.

Polyfill dla IE:

if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
    var el = this;
    while (el) {
        if (el.matches(selector)) {
            return el;
        }
        el = el.parentElement;
    }
};

Zauważ, że nie ma, returngdy element nie został znaleziony, skutecznie zwracając, undefinedgdy nie znaleziono najbliższego elementu.

Więcej informacji można znaleźć pod adresem : https://developer.mozilla.org/en-US/docs/Web/API/Element/closest


4
Dlaczego ta odpowiedź jest na dole strony? Powinien być na górze.
Wielkie

Najbliższy jest w jQuery, ale OP mówi, że nie ma jQuery.
steve moretz

@stevemoretz. najbliżej jest teraz natywny JavaScript
Louise Eggleton

@LouiseEggleton Tak jest, ups
steve moretz

To się nazywa najlepsza odpowiedź!
ChosenUser

22

Oto jak uzyskać najbliższy element według nazwy tagu bez jQuery:

function getClosest(el, tag) {
  // this is necessary since nodeName is always in upper case
  tag = tag.toUpperCase();
  do {
    if (el.nodeName === tag) {
      // tag name is found! let's return it. :)
      return el;
    }
  } while (el = el.parentNode);

  // not found :(
  return null;
}

getClosest(th, 'tbody');

2
Nie wierzę, że to zadziała. Sprawdza tylko drzewo DOM. th-> thead-> table nigdy nie bierze pod uwagę rodzeństwa
hunterc

2
Powinieneś był podać ten konkretny przypadek w swoim pytaniu.
Joon

2
Popatrz. Ta funkcja przeszukuje parentNodes, aby znaleźć najbliższego rodzica (nawet według rozszerzenia), który używa podanego tagu. To nie przejdzie „w dół” drzewa dokumentów, tylko „w górę”. Przeciętny użytkownik najprawdopodobniej zobaczy „najbliższy” węzeł jako najbliższy brat. Po prostu nie zna terminologii, której potrzebował.

1
Aby uczciwie odnosić się do @Jhawins, twoja definicja najbliższego jest tym, co najbliższe terminom jQuery. To nie jest pytanie jQuery. Nie mogę zaświadczyć, dlaczego jQuery zdecydowało, że najbliższy oznacza najbliższego przodka, ale bardziej rozsądna definicja byłaby najbliższym elementem, niezależnie od tego, czy będzie to rodzic, poprzednie rodzeństwo, następne rodzeństwo itp. Cokolwiek zostanie znalezione „najbliżej” elementu docelowego.
ryandlf

1
@ryandlf Ale co by było, gdybyś miał zarówno rodzica, jak i rodzeństwo w tej samej „odległości”? Definicja jQuery jest jasna, ponieważ zwróci najwyżej jedno dopasowanie.
mtone

9

Do tego celu służy standardowa funkcja: Element.closest . Obsługuje ją większość przeglądarek z wyjątkiem IE11 ( szczegóły na caniuse.com ). Dokumenty MDN zawierają również polyfill na wypadek, gdybyś musiał kierować reklamy na starsze przeglądarki.

Aby znaleźć najbliższego tbodyrodzica, który dostaniesz, thmożesz zrobić:

th.closest('tbody');

Jeśli chcesz sam napisać funkcję - oto co wymyśliłem:

function findClosestParent (startElement, fn) {
  var parent = startElement.parentElement;
  if (!parent) return undefined;
  return fn(parent) ? parent : findClosestParent(parent, fn);
}

Aby znaleźć najbliższego rodzica według nazwy tagu, możesz go użyć w następujący sposób:

findClosestParent(x, element => return element.tagName === "SECTION");

6
function closest(el, sel) {
    if (el != null)
        return el.matches(sel) ? el 
            : (el.querySelector(sel) 
                || closest(el.parentNode, sel));
}

To rozwiązanie wykorzystuje niektóre z nowszych funkcji specyfikacji HTML 5 i używanie go w starszych / niekompatybilnych przeglądarkach (czytaj: Internet Explorer) będzie wymagało wypełnienia.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
    || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
    || Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);

Zawsze trafia do pierwszego stołu. nie najbliższy stolik .. zobacz ten link jsfiddle.net/guuy5kof
Balachandran

dobry chwyt! naprawiono, patrz tutaj jsfiddle.net/c2pqc2x0, proszę, zagłosuj na mnie jak szef :)
Matthew James Davis

tak, źle ... Czy sprawdziłeś swój wynik na skrzypcach, pokazuje błąd .. dopasowania undefined
Balachandran

działa świetnie dla mnie, w której przeglądarce pracujesz? nie jest to obsługiwane w starszych przeglądarkach
Matthew James Davis,

hej, wyjaśnijmy, że przegłosuj, co mówisz brachu
Matthew James Davis

3

Aby przedłużyć odpowiedź @SalmanPK

Pozwoli to na użycie węzła jako selektora, przydatne podczas pracy z takimi zdarzeniami jak najechanie kursorem myszy.

function closest(el, selector) {
    if (typeof selector === 'string') {
        matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');
        while (el.parentElement) {
            if (el[matches](selector)) {
                return el
            };
            el = el.parentElement;
        }
    } else {
        while (el.parentElement) {
            if (el === selector) {
                return el
            };
            el = el.parentElement;
        }
    }

    return null;
}

2

Oto prosta funkcja, której używam: -

function closest(el, selector) {
    var matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');

    while (el.parentElement) {
        if (el[matches](selector)) return el;

        el = el.parentElement;
    }

    return null;
}

2

Podsumowanie:

Aby znaleźć konkretnego przodka, możemy użyć:

Element.closest();

Ta funkcja przyjmuje jako argument ciąg selektora CSS. następnie zwraca najbliższego przodka bieżącego elementu (lub samego elementu), który pasuje do selektora CSS przekazanego w argumentach. Jeśli nie ma przodka, powróci null.

Przykład:

const child = document.querySelector('.child');
// select the child

console.dir(child.closest('.parent').className);
// check if there is any ancestor called parent
<div class="parent">
  <div></div>
  <div>
    <div></div>
    <div class="child"></div>
  </div>
</div>


1

Pobierz najbliższy element DOM w drzewie, który zawiera klasę, identyfikator, atrybut danych lub tag. Zawiera sam element. Obsługiwane z powrotem do IE6.

var getClosest = function (elem, selector) {

    var firstChar = selector.charAt(0);

    // Get closest match
    for ( ; elem && elem !== document; elem = elem.parentNode ) {

        // If selector is a class
        if ( firstChar === '.' ) {
            if ( elem.classList.contains( selector.substr(1) ) ) {
                return elem;
            }
        }

        // If selector is an ID
        if ( firstChar === '#' ) {
            if ( elem.id === selector.substr(1) ) {
                return elem;
            }
        } 

        // If selector is a data attribute
        if ( firstChar === '[' ) {
            if ( elem.hasAttribute( selector.substr(1, selector.length - 2) ) ) {
                return elem;
            }
        }

        // If selector is a tag
        if ( elem.tagName.toLowerCase() === selector ) {
            return elem;
        }

    }

    return false;

};

var elem = document.querySelector('#some-element');
var closest = getClosest(elem, '.some-class');
var closestLink = getClosest(elem, 'a');
var closestExcludingElement = getClosest(elem.parentNode, '.some-class');

Dlaczego nie użyć przełącznika firstCharzamiast wszystkich IFwarunków?
Rafael Herscovici

Po co używać zasłoniętej forpętli?
Rafael Herscovici

1

Znajdź najbliższe childNodes Elements.

closest:function(el, selector,userMatchFn) {
var matchesFn;

// find vendor prefix
['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
    if (typeof document.body[fn] == 'function') {
        matchesFn = fn;
        return true;
    }
    return false;
});
function findInChilds(el){
    if(!el) return false;
    if(el && el[matchesFn] && el[matchesFn](selector)

    && userMatchFn(el) ) return [el];
    var resultAsArr=[];
    if(el.childNodes && el.childNodes.length){
        for(var i=0;i< el.childNodes.length;i++)
        {
             var child=el.childNodes[i];
             var resultForChild=findInChilds(child);
            if(resultForChild instanceof Array){
                for(var j=0;j<resultForChild.length;j++)
                {
                    resultAsArr.push(resultForChild[j]);
                }
            } 
        }

    }
    return resultAsArr.length?resultAsArr: false;
}

var parent;
if(!userMatchFn || arguments.length==2) userMatchFn=function(){return true;}
while (el) {
    parent = el.parentElement;
    result=findInChilds(parent);
    if (result)     return result;

    el = parent;
}

return null;

}


0

Tutaj.

function findNearest(el, tag) {
    while( el && el.tagName && el.tagName !== tag.toUpperCase()) {
        el = el.nextSibling;     
    } return el;
} 

Znajduje tylko rodzeństwo w dalszej części drzewa. Użyj previousSibling, aby przejść w drugą stronę. Lub użyj zmiennych, aby przejść w obie strony i zwrócić tę z nich, która zostanie znaleziona jako pierwsza. Masz ogólny pomysł, ale jeśli chcesz przejść przez parentNodes lub child, jeśli rodzeństwo nie pasuje, równie dobrze możesz użyć jQuery. W tym momencie jest to łatwo tego warte.


0

Trochę spóźniłem się na imprezę, ale gdy przechodziłem obok i odpowiadałem tylko na bardzo podobne pytanie, rzucam tutaj moje rozwiązanie - możemy powiedzieć, że jest to closest()podejście JQuery , ale w zwykłym dobrym JavaScript.

Nie potrzebuje żadnych pollyfillów i jest to starsze przeglądarki i przyjazne dla IE (:-)): https://stackoverflow.com/a/48726873/2816279


-4

Myślę, że najłatwiejszy kod do złapania z najbliższym jquery:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $(".add").on("click", function () {
            var v = $(this).closest(".division").find("input[name='roll']").val();
            alert(v);
        });
    });
</script>
<?php

for ($i = 1; $i <= 5; $i++) {
    echo'<div class = "division">'
        . '<form method="POST" action="">'
        . '<p><input type="number" name="roll" placeholder="Enter Roll"></p>'
        . '<p><input type="button" class="add" name = "submit" value = "Click"></p>'
        . '</form></div>';
}
?>

Dzięki wielkie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.