Trouver l'élément le plus proche sans jQuery

90

J'essaie de trouver l'élément le plus proche avec un nom de balise spécifique sans jquery. Lorsque je clique sur un, <th>je veux accéder au <tbody>pour cette table. Suggestions? J'ai lu sur l'offset mais je ne l'ai pas trop compris. Dois-je simplement utiliser:

Supposons que l'élément soit déjà défini sur l'élément cliqué

th.offsetParent.getElementsByTagName('tbody')[0]
chasseur
la source
1
Si vous trouvez que vous devez commencer à parcourir le DOM, c'est une instance où les kbs supplémentaires attribués à jquery en valent la peine.
Kevin Bowersox
1
essayez parentNode: developer.mozilla.org/en-US/docs/Web/API/Node.parentNode
Ivan Chernykh
2
Je pense que c'est une question très importante et valable. Il n'y a aucune raison de voter contre.
el.closest('tbody')pour les navigateurs non-ie. Voir la réponse plus élaborée + polyfill ci-dessous.
oriadam

Réponses:

69

Peu (très) tard à la fête, mais néanmoins. Cela devrait faire l' affaire :

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;
}
Alès
la source
2
Excellente réponse qui fonctionne un régal. MDN a également un polyfill pour element.closest (). Chrome inclut element.matches () dans la version actuelle, donc aucun préfixe n'est nécessaire. Je viens de l'ajouter à une bibliothèque que j'utilise dans une application que je développe, Clibu.
nevf
2
J'ai changé le code pour qu'il teste également l'élément elque jQuery.closest () et Element.closest () font. for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nevf
1
Ce code est bien, mais il manque un point-virgule et définit également parentdans la portée globale (!)
Steven Lu
1
Cela vaut la peine de mentionner: developer.mozilla.org/en-US/docs/Web/API/Element/closest la méthode native pour le support le plus proche, bien que peu faible: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D
Juste une note à ce sujet, vous voudrez peut-être faire el.parentNodeou cela se cassera lors de la traversée d'un SVG dans IE.
smcka
124

Très simple:

el.closest('tbody')

Pris en charge sur tous les navigateurs sauf IE.
MISE À JOUR : Edge le prend désormais également en charge.

Pas besoin de jQuery. De plus, le remplacement de jQuery $(this).closest('tbody')par $(this.closest('tbody'))augmentera les performances, de manière significative lorsque l'élément n'est pas trouvé.

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

Notez qu'il n'y a pas returnlorsque l'élément n'a pas été trouvé, retournant effectivement undefinedlorsque l'élément le plus proche n'a pas été trouvé.

Pour plus de détails, voir: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

oriadam
la source
4
Pourquoi cette réponse est-elle au bas de la page? Devrait être au sommet. Merci beaucoup
Julien Le Coupanec
le plus proche est dans jQuery, mais OP ne dit pas de jQuery.
steve moretz
@stevemoretz. le plus proche est natif JavaScript maintenant
Louise Eggleton
@LouiseEggleton Yup it is, oops
Steve Moretz
C'est ce qu'on appelle la meilleure réponse!
ChosenUser
22

Voici comment obtenir l'élément le plus proche par nom de balise sans 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
la source
2
Je ne pense pas que cela fonctionnerait. Il vérifie uniquement l'arborescence DOM. th-> thead-> table ne considère jamais les frères
hunterc
2
Vous auriez dû indiquer ce cas précis dans votre question.
Joon
2
Regardez. Cette fonction parcourt parentNodes pour trouver le parent le plus proche (même par extension) qui utilise la balise fournie. Cela ne va pas «vers le bas» dans l'arborescence des documents, seulement «vers le haut». L'utilisateur moyen verrait probablement le nœud le plus proche comme le frère le plus proche. Il ne connaît tout simplement pas la terminologie dont il avait besoin.
1
Pour être juste envers @Jhawins, votre définition du plus proche est ce que jQuery appelle le plus proche. Ce n'est pas une question jQuery. Je ne peux pas attester pourquoi jQuery a décidé que le plus proche signifiait l'ancêtre le plus proche, mais une définition plus raisonnable serait l'élément le plus proche que ce soit un parent, un frère précédent, un frère suivant, etc. Tout ce qui est trouvé "le plus proche" de l'élément cible.
ryandlf
1
@ryandlf Mais que se passerait-il si vous aviez à la fois un parent et un frère ou une sœur à la même «distance»? La définition de jQuery est claire en ce qu'elle renverra une correspondance au plus.
mtone
9

Il existe une fonction standardisée pour ce faire: Element.closest . La plupart des navigateurs, à l'exception d'IE11, le prennent en charge ( détails par caniuse.com ). Les documents MDN incluent également un polyfill au cas où vous auriez à cibler des navigateurs plus anciens.

Pour trouver le tbodyparent le plus proche, thvous pouvez faire:

th.closest('tbody');

Au cas où vous voudriez écrire la fonction vous-même - voici ce que j'ai proposé:

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

Pour trouver le parent le plus proche par nom de balise, vous pouvez l'utiliser comme ceci:

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

Cette solution utilise certaines des fonctionnalités les plus récentes de la spécification HTML 5, et son utilisation sur des navigateurs plus anciens / incompatibles (lire: Internet Explorer) nécessitera un polyfill.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
    || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
    || Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);
Matthew James Davis
la source
Il va toujours en tête de la première table. table non la plus proche .. voir ce lien jsfiddle.net/guuy5kof
Balachandran
Belle prise! corrigé, voir ici jsfiddle.net/c2pqc2x0 s'il vous plaît upvote moi comme un patron :)
Matthew James Davis
ya i ill do ... Avez-vous vérifié le résultat de votre violon, il affiche une erreur .. correspond à undefined
Balachandran
fonctionne très bien pour moi, dans quel navigateur travaillez-vous? cela n'est pas pris en charge dans les anciens navigateurs
Matthew James Davis
hey laisse clair ce vote négatif que dites-vous bro
Matthew James Davis
3

Pour étendre la réponse @SalmanPK

cela permettra d'utiliser node comme sélecteur, utile lorsque vous travaillez avec des événements comme le survol de la souris.

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;
}
zb '
la source
2

Voici la fonction simple que j'utilise: -

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
la source
2

Sommaire:

Pour trouver un ancêtre particulier, nous pouvons utiliser:

Element.closest();

Cette fonction prend une chaîne de sélection CSS comme argument. il retourne alors l'ancêtre le plus proche de l'élément courant (ou de l'élément lui-même) qui correspond au sélecteur CSS qui a été passé dans les arguments. S'il n'y a pas d'ancêtre, il reviendra null.

Exemple:

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
la source
1

Obtenez l'élément DOM le plus proche dans l'arborescence qui contient une classe, un ID, un attribut de données ou une balise. Inclut l'élément lui-même. Prise en charge vers 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');
Chris Ferdinandi
la source
Pourquoi ne pas utiliser un commutateur pour le firstCharau lieu de toutes les IFconditions?
Rafael Herscovici
Pourquoi utiliser une forboucle masquée ?
Rafael Herscovici
1

Trouvez les childNodes Eléments les plus proches.

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
la source
0

Ici.

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

Ne trouve que les frères et sœurs plus bas dans l'arbre. Utilisez previousSibling pour aller dans l'autre sens Ou utilisez des variables pour parcourir les deux sens et renvoyer celui qui est trouvé en premier. Vous avez l'idée générale, mais si vous voulez traverser les nœuds parents ou les enfants si un frère ne correspond pas, vous pouvez également utiliser jQuery. À ce stade, cela en vaut la peine.

RonnyKnoxville
la source
0

Un peu tard à la fête, mais alors que je passais et que je répondais juste à une question très similaire, je laisse ici ma solution - nous pouvons dire que c'est l' closest()approche JQuery , mais en bon vieux JavaScript.

Il n'a pas besoin de pollyfills et ses navigateurs plus anciens et compatibles avec IE (:-)): https://stackoverflow.com/a/48726873/2816279

Pedro Ferreira
la source
-4

Je pense que le code le plus simple à attraper avec jquery le plus proche:

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

Merci beaucoup.

AL MaMun
la source