Finden des nächsten Elements ohne jQuery

88

Ich versuche, das nächstgelegene Element mit einem bestimmten Tag-Namen ohne jquery zu finden. Wenn ich auf ein <th>klicke, möchte ich Zugriff auf das <tbody>für diese Tabelle erhalten. Vorschläge? Ich habe über Offset gelesen, aber nicht wirklich verstanden. Soll ich nur verwenden:

Angenommen, th ist bereits auf das angeklickte Element eingestellt

th.offsetParent.getElementsByTagName('tbody')[0]
hunterc
quelle
1
Wenn Sie feststellen, dass Sie mit dem Durchlaufen des DOM beginnen müssen, ist dies eine Instanz, in der sich die zusätzlichen kbs, die jquery zugeschrieben werden, lohnen würden.
Kevin Bowersox
1
versuchen Sie parentNode: developer.mozilla.org/en-US/docs/Web/API/Node.parentNode
Cherniv
2
Ich denke, das ist eine sehr wichtige und berechtigte Frage. Es gibt keinen Grund für Abstimmungen.
el.closest('tbody')für Nicht-Browser. Weitere Informationen zu Antworten + Polyfill finden Sie weiter unten.
Oriadam

Antworten:

69

Wenig (sehr) spät zur Party, aber trotzdem. Dies sollte den Trick tun :

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;
}
Ales
quelle
2
Tolle Antwort, die ein Vergnügen ist. MDN hat auch eine Polyfüllung für element.closest (). Chrome enthält element.matches () in der aktuellen Version, sodass kein Präfix erforderlich ist. Ich habe dies gerade zu einer Bibliothek hinzugefügt, die ich in einer App verwende, die ich entwickle, Clibu.
Nevf
2
Ich habe den Code so geändert, dass er auch Elemente testet, eldie jQuery.closest () und auch Element.closest () ausführen. for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
Nevf
1
Dieser Code ist in Ordnung, aber es fehlt ein Semikolon und es wird auch parentim globalen Bereich (!) Definiert
Steven Lu
1
Erwähnenswert sein könnte: developer.mozilla.org/en-US/docs/Web/API/Element/closest die native Methode für die engste, wenn auch wenig geringe Unterstützung: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D.
Nur ein Hinweis dazu, den Sie möglicherweise tun möchten, el.parentNodeoder dies wird beim Durchlaufen einer SVG im IE unterbrochen.
Smcka
120

Sehr einfach:

el.closest('tbody')

Wird von allen Browsern außer IE unterstützt.
UPDATE : Edge unterstützt es jetzt auch.

Keine Notwendigkeit für jQuery. Wenn Sie jQuery's $(this).closest('tbody')durch ersetzen, $(this.closest('tbody'))wird die Leistung erheblich gesteigert, wenn das Element nicht gefunden wird.

Polyfill für 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;
    }
};

Beachten Sie, dass es keine gibt, returnwenn das Element nicht gefunden wurde, und effektiv zurückkehrt, undefinedwenn das nächstgelegene Element nicht gefunden wurde.

Weitere Informationen finden Sie unter: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

Oriadam
quelle
4
Warum steht diese Antwort am Ende der Seite? Sollte oben sein. Vielen Dank
Julien Le Coupanec
am nächsten ist in jQuery. Aber OP sagt kein jQuery.
Steve Moretz
@stevemoretz. Am nächsten ist jetzt natives JavaScript
Louise Eggleton
@ LouisEggleton Ja, es ist, oops
Steve Moretz
Das nennt man die beste Antwort!
Md. Tahazzot
22

So erhalten Sie das nächstgelegene Element anhand des Tag-Namens ohne 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');
Joon
quelle
2
Ich glaube nicht, dass das funktionieren würde. Es wird nur der DOM-Baum überprüft. th-> thead-> table berücksichtigt niemals Geschwister
hunterc
2
Sie sollten diesen speziellen Fall in Ihrer Frage angeben.
Joon
2
Aussehen. Diese Funktion durchläuft parentNodes, um das nächstgelegene übergeordnete Element (auch nach Erweiterung) zu finden, das das bereitgestellte Tag verwendet. Dadurch wird der Dokumentbaum nicht "herunter", sondern nur "hoch". Der durchschnittliche Benutzer würde den "nächstgelegenen" Knoten höchstwahrscheinlich als das nächstgelegene Geschwister sehen. Er kennt einfach nicht die Terminologie, die er brauchte.
1
Um @Jhawins gerecht zu werden, ist Ihre Definition von am nächsten das, was jQuery am nächsten bringt. Dies ist keine jQuery-Frage. Ich kann nicht bezeugen, warum jQuery entschieden hat, dass der nächste Vorfahr am nächsten ist, aber eine vernünftigere Definition wäre das nächstgelegene Element, sei es ein Elternteil, ein vorheriges Geschwister, ein nächstes Geschwister usw. Was auch immer "am nächsten" zum Zielelement gefunden wird.
Ryandlf
1
@ryandlf Aber was wäre, wenn Sie sowohl einen Elternteil als auch ein Geschwister in derselben "Entfernung" hätten? Die Definition von jQuery ist klar, dass höchstens eine Übereinstimmung zurückgegeben wird.
Ton
9

Hierzu gibt es eine standardisierte Funktion: Element.closest . Die meisten Browser außer IE11 unterstützen dies ( Details von caniuse.com ). Die MDN-Dokumente auch eine Polyfüllung, falls Sie auf ältere Browser abzielen müssen.

Um den nächsten tbodyElternteil zu finden, können thSie Folgendes tun:

th.closest('tbody');

Für den Fall, dass Sie die Funktion selbst schreiben möchten - hier ist, was ich mir ausgedacht habe:

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

Um das nächstgelegene übergeordnete Element anhand des Tag-Namens zu finden, können Sie es folgendermaßen verwenden:

findClosestParent(x, element => return element.tagName === "SECTION");
david1995
quelle
6
function closest(el, sel) {
    if (el != null)
        return el.matches(sel) ? el 
            : (el.querySelector(sel) 
                || closest(el.parentNode, sel));
}

Diese Lösung verwendet einige der neueren Funktionen der HTML 5-Spezifikation. Für die Verwendung dieser Funktion in älteren / inkompatiblen Browsern (sprich: Internet Explorer) ist eine Polyfüllung erforderlich.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
    || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
    || Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);
Matthew James Davis
quelle
Es geht immer um den ersten Tisch. nicht nächstgelegene Tabelle .. siehe diesen Link jsfiddle.net/guuy5kof
Balachandran
schöner Fang! behoben, siehe hier jsfiddle.net/c2pqc2x0 bitte stimmen Sie mich wie ein Chef :)
Matthew James Davis
ya ich werde tun ... Haben Sie Ihr Geigenergebnis überprüft, zeigt es Fehler ..matches undefined
Balachandran
funktioniert gut für mich, in welchem ​​Browser arbeitest du? Dies wird in älteren Browsern nicht unterstützt
Matthew James Davis
Hey, lass uns klarstellen, was du sagst, Bruder
Matthew James Davis
3

Um die @ SalmanPK-Antwort zu erweitern

Sie können den Knoten als Selektor verwenden, was nützlich ist, wenn Sie mit Ereignissen wie Mouseover arbeiten.

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;
}
Zum Beispiel'
quelle
2

Hier ist die einfache Funktion, die ich benutze: -

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;
}
Salman von Abbas
quelle
2

Zusammenfassung:

Um einen bestimmten Vorfahren zu finden, können wir verwenden:

Element.closest();

Diese Funktion verwendet eine CSS-Auswahlzeichenfolge als Argument. Anschließend wird der nächste Vorfahr des aktuellen Elements (oder des Elements selbst) zurückgegeben, der dem CSS-Selektor entspricht, der in den Argumenten übergeben wurde. Wenn es keinen Vorfahren gibt, wird er zurückkehren null.

Beispiel:

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>

Willem van der Veen
quelle
1

Holen Sie sich das nächstgelegene DOM-Element in den Baum, das eine Klasse, ID, ein Datenattribut oder ein Tag enthält. Beinhaltet das Element selbst. Zurück zu IE6 unterstützt.

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');
Chris Ferdinandi
quelle
Warum nicht einen Schalter für die firstCharstatt aller IFBedingungen verwenden?
Dementic
Warum eine verdeckte forSchleife verwenden?
Dementic
1

Finden Sie die nächstgelegenen Elemente childNodes.

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;

}}

MajidTaheri
quelle
0

Hier.

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

Findet nur Geschwister weiter unten im Baum. Verwenden Sie previousSibling , um den anderen Weg zu gehen, oder verwenden Sie Variablen, um beide Wege zu durchlaufen und den zuerst gefundenen zurückzugeben. Sie haben eine allgemeine Vorstellung davon, aber wenn Sie Elternknoten oder Kinder durchlaufen möchten, wenn ein Geschwister nicht übereinstimmt, können Sie auch jQuery verwenden. An diesem Punkt lohnt es sich leicht.

RonnyKnoxville
quelle
0

Ein bisschen zu spät zur Party, aber als ich vorbeikam und nur eine sehr ähnliche Frage beantwortete, lasse ich hier meine Lösung fallen - wir können sagen, es ist die JQuery closest() Ansatz, aber in einfach gutem alten JavaScript.

Es benötigt keine Pollyfills und es sind ältere Browser und IE (:-)) freundlich: https://stackoverflow.com/a/48726873/2816279

Pedro Ferreira
quelle
-4

Ich denke, der einfachste Code, den man mit jquery am nächsten fangen kann:

<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>';
}
?>

Vielen Dank.

AL MaMun
quelle