Trovare l'elemento più vicino senza jQuery


90

Sto cercando di trovare l'elemento più vicino con un nome di tag specifico senza jquery. Quando clicco su una <th>desidero accedere a <tbody>quella tabella. Suggerimenti? Ho letto dell'offset ma non l'ho capito molto. Dovrei usare solo:

Supponiamo che sia già impostato sull'elemento cliccato

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

1
Se scopri che devi iniziare ad attraversare il DOM, questa è un'istanza in cui varrebbe la pena i kb extra attribuiti a jquery.
Kevin Bowersox


2
Penso che questa sia una domanda molto importante e valida. Non c'è motivo per voti negativi.

el.closest('tbody')per browser diversi da ie. Vedi risposta più elaborata + polyfill di seguito.
oriadam

Risposte:


69

Poco (molto) tardi per la festa, ma comunque. Questo dovrebbe fare il trucco :

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
Ottima risposta che funziona a meraviglia. MDN ha anche un polyfill per element.closest (). Chrome include element.matches () nella versione corrente, quindi non è necessario alcun prefisso. L'ho appena aggiunto a una libreria che sto usando in un'app che sto sviluppando, Clibu.
nef

2
Ho cambiato il codice in modo che verifichi anche l'elemento elche fanno jQuery.closest () e anche Element.closest (). for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nef

1
Questo codice va bene, ma manca un punto e virgola e definisce anche parentnell'ambito globale (!)
Steven Lu

1
Potrebbe valere la pena menzionare: developer.mozilla.org/en-US/docs/Web/API/Element/closest il metodo nativo per il più vicino, anche se con poco supporto: Chrome41, FF35, IE-no, Opera28, Safari9
Michiel D

Solo una nota su questo, potresti voler fare el.parentNodeo questo si interromperà quando attraversi un SVG in IE.
smcka

124

Molto semplice:

el.closest('tbody')

Supportato su tutti i browser tranne IE.
AGGIORNAMENTO : anche Edge ora lo supporta.

Non c'è bisogno di jQuery. Inoltre, la sostituzione di jQuery $(this).closest('tbody')con $(this.closest('tbody'))aumenterà le prestazioni, in modo significativo quando l'elemento non viene trovato.

Polyfill per 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;
    }
};

Nota che non c'è returnquando l'elemento non è stato trovato, in effetti restituisce undefinedquando l'elemento più vicino non è stato trovato.

Per maggiori dettagli vedere: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest


4
Perché questa risposta è in fondo alla pagina? Dovrebbe essere in cima. Grazie mille
Julien Le Coupanec

il più vicino è in jQuery, ma OP dice no jQuery.
steve moretz

@stevemoretz. il più vicino è JavaScript nativo ora
Louise Eggleton

@LouiseEggleton Sì, oops
steve moretz

Questa è la risposta migliore!
ChosenUser

22

Ecco come ottenere l'elemento più vicino in base al nome del tag senza 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
Non credo che funzionerebbe. Controlla solo l'albero DOM. th-> thead-> table non considera mai i fratelli
hunterc

2
Avresti dovuto indicare quel caso specifico nella tua domanda.
Joon

2
Guarda. Questa funzione attraversa parentNodes per trovare il genitore più vicino (anche per estensione) che utilizza il tag fornito. Questo non andrà "giù" nell'albero del documento, ma solo "su". L'utente medio vedrebbe il nodo "più vicino" molto probabilmente come il fratello più vicino. Semplicemente non conosce la terminologia di cui aveva bisogno.

1
In tutta onestà con @Jhawins, la tua definizione di più vicino è ciò che i termini jQuery più vicini. Questa non è una domanda su jQuery. Non posso attestare il motivo per cui jQuery ha deciso che il più vicino significava l'antenato più vicino, ma una definizione più ragionevole sarebbe l'elemento più vicino che si tratti di un genitore, un fratello precedente, un fratello successivo ecc. Qualunque cosa si trovi "più vicino" all'elemento di destinazione.
ryandlf

1
@ryandlf Ma cosa succederebbe se avessi sia un genitore che un fratello alla stessa "distanza"? La definizione di jQuery è chiara in quanto restituirà al massimo una corrispondenza.
mtone

9

Esiste una funzione standardizzata per farlo: Element.closest . La maggior parte dei browser tranne IE11 lo supportano ( dettagli di caniuse.com ). I documenti MDN includono anche un polyfill nel caso in cui sia necessario scegliere come target browser meno recenti.

Per trovare il tbodygenitore più vicino a cui è stato dato un thpotresti fare:

th.closest('tbody');

Nel caso in cui desideri scrivere la funzione da solo, ecco cosa ho pensato:

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

Per trovare il genitore più vicino in base al nome del tag puoi usarlo in questo modo:

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

Questa soluzione utilizza alcune delle funzionalità più recenti della specifica HTML 5 e l'utilizzo di questa su browser meno recenti / incompatibili (leggi: Internet Explorer) richiederà un polyfill.

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

Va sempre in cima al primo tavolo. tavolo non più vicino .. vedi questo link jsfiddle.net/guuy5kof
Balachandran

bella presa! risolto, vedi qui jsfiddle.net/c2pqc2x0 per favore votami come un capo :)
Matthew James Davis

Sì, lo farò ... Hai controllato il risultato del violino, mostra un errore .. corrispondenze indefinite
Balachandran

funziona alla grande per me, con quale browser stai lavorando? questo non è supportato nei browser meno recenti
Matthew James Davis

hey chiariamo che downvote cosa dici fratello
Matthew James Davis

3

Per estendere la risposta @SalmanPK

consentirà di utilizzare il nodo come selettore, utile quando si lavora con eventi come il mouseover.

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

Ecco la semplice funzione che sto usando: -

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

Sommario:

Per trovare un particolare antenato possiamo usare:

Element.closest();

Questa funzione accetta una stringa di selettore CSS come argomento. restituisce quindi l'antenato più vicino dell'elemento corrente (o l'elemento stesso) che corrisponde al selettore CSS che è stato passato negli argomenti. Se non ci sono antenati tornerà null.

Esempio:

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

Ottieni l'elemento DOM più vicino all'albero che contiene una classe, un ID, un attributo di dati o un tag. Include l'elemento stesso. Supportato indietro a 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');

Perché non utilizzare un interruttore per il firstCharinvece di tutte le IFcondizioni?
Rafael Herscovici

Perché utilizzare un forloop nascosto ?
Rafael Herscovici

1

Trova gli Elements childNodes più vicini.

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

Qui.

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

Trova solo fratelli più in basso nell'albero. Usa previousSibling per andare dall'altra parte Oppure usa le variabili per attraversare in entrambe le direzioni e tornare a seconda di quale si trova per prima. Hai un'idea generale, ma se vuoi attraversare parentNodes o figli se un fratello non corrisponde, puoi anche usare jQuery. A quel punto ne vale facilmente la pena.


0

Un po 'tardi per la festa, ma mentre stavo passando e rispondo a una domanda molto simile, lascio qui la mia soluzione: possiamo dire che è l' closest()approccio JQuery , ma in buona sostanza JavaScript.

Non ha bisogno di pollyfill ed è un browser meno recente e compatibile con IE (:-)): https://stackoverflow.com/a/48726873/2816279


-4

Penso che il codice più semplice da catturare con jquery più vicino:

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

Grazie mille

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.