Поиск ближайшего элемента без jQuery

90

Я пытаюсь найти ближайший элемент с определенным именем тега без jquery. Когда я нажимаю на, <th>я хочу получить доступ к <tbody>этой таблице. Предложения? Я читал о смещении, но толком не разбирался. Должен ли я просто использовать:

Предположим, что th уже настроен на нажатый элемент th

th.offsetParent.getElementsByTagName('tbody')[0]
охотник
источник
1
Если вы обнаружите, что вам необходимо начать обход через DOM, это тот случай, когда дополнительные килобайт, приписываемые jquery, того стоят.
Кевин Бауэрсокс,
1
попробуйте parentNode: developer.mozilla.org/en-US/docs/Web/API/Node.parentNode
Иван Черных
2
Я считаю, что это очень важный и актуальный вопрос. Нет причин для отрицательных голосов.
el.closest('tbody')для браузеров, отличных от IE. См. Более подробный ответ + полифил ниже.
oriadam

Ответы:

69

Немного (очень) поздно на вечеринку, но тем не менее. Это следует сделать трюк :

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
Отличный ответ, который работает. В MDN также есть полифил для element.closest (). Chrome включает element.matches () в текущую версию, поэтому префикс не требуется. Я только что добавил это в библиотеку, которую использую в разрабатываемом мной приложении, Clibu.
nevf
2
Я изменил код, чтобы он также elпроверял элемент, который выполняет jQuery.closest (), а также Element.closest (). for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nevf
1
Этот код хорош, но в нем отсутствует точка с запятой и он также определяется parentв глобальной области видимости (!)
Стивен Лу
1
Возможно, стоит упомянуть: developer.mozilla.org/en-US/docs/Web/API/Element/closest собственный метод для ближайших, хотя и с небольшой поддержкой: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D
Просто обратите внимание на это, вы можете захотеть это сделать, el.parentNodeиначе это сломается при обходе SVG в IE.
smcka
125

Очень просто:

el.closest('tbody')

Поддерживается во всех браузерах, кроме IE.
ОБНОВЛЕНИЕ : Edge теперь также поддерживает его.

Нет необходимости в jQuery. Более того , замена JQuery это $(this).closest('tbody')с $(this.closest('tbody'))увеличит производительность, существенно , когда элемент не найден.

Полифилл для 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;
    }
};

Обратите внимание, что нет, returnкогда элемент не был найден, эффективно возвращается, undefinedкогда ближайший элемент не был найден.

Подробнее см. Https://developer.mozilla.org/en-US/docs/Web/API/Element/closest.

Ориадам
источник
4
Почему этот ответ находится внизу страницы? Должно быть наверху. Большое спасибо
Жюльен Ле Купанек
ближайший находится в jQuery, но OP не говорит, что jQuery.
Стив Моретц
@stevemoretz. Ближайшим сейчас является нативный JavaScript
Луиза Эгглтон,
@LouiseEggleton Да, это так, ой
Стив Моретц
Это лучший ответ!
ChosenUser
22

Вот как можно получить ближайший элемент по имени тега без 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
Я не верю, что это сработает. Он только проверяет дерево DOM. th-> thead-> table никогда не считает братьев и сестер
hunterc 06
2
Вы должны указать этот конкретный случай в своем вопросе.
Джун
2
Смотреть. Эта функция просматривает parentNodes, чтобы найти ближайшего родителя (даже по расширению), который использует предоставленный тег. Это не будет идти «вниз» по дереву документа, только «вверх». Средний пользователь будет видеть «ближайший» узел, скорее всего, как ближайшего брата. Он просто не знает нужной терминологии.
1
Честно говоря, @Jhawins ваше определение ближайшего - это то, что jQuery термины наиболее близки. Это не вопрос jQuery. Я не могу засвидетельствовать, почему jQuery решил, что ближайший означает ближайшего предка, но более разумным определением будет ближайший элемент, будь то родительский элемент, предыдущий брат, следующий брат и т. Д. Все, что найдено «ближайшим» к целевому элементу.
ryandlf
1
@ryandlf Но что, если бы у вас были родители и брат или сестра на одном «расстоянии»? Определение jQuery ясно, что он вернет не более одного совпадения.
mtone
9

Для этого существует стандартизированная функция: Element.closest . Большинство браузеров, кроме IE11, поддерживают его ( подробности см . На caniuse.com ). Документы MDN также включают полифил на случай, если вам нужно настроить таргетинг на старые браузеры.

Чтобы найти ближайшего tbodyродителя с учетом a, thвы можете:

th.closest('tbody');

Если вы хотите написать функцию самостоятельно - вот что я придумал:

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

Чтобы найти ближайшего родителя по имени тега, вы можете использовать его так:

findClosestParent(x, element => return element.tagName === "SECTION");
david1995
источник
6
function closest(el, sel) {
    if (el != null)
        return el.matches(sel) ? el 
            : (el.querySelector(sel) 
                || closest(el.parentNode, sel));
}

В этом решении используются некоторые из новейших функций спецификации HTML 5, и для их использования в старых / несовместимых браузерах (читай: Internet Explorer) потребуется полифил.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
    || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
    || Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);
Мэтью Джеймс Дэвис
источник
Он всегда идет вверх первой таблицы. не ближайшая таблица .. см. эту ссылку jsfiddle.net/guuy5kof
Balachandran
Хорошо поймал! исправлено, см. здесь jsfiddle.net/c2pqc2x0, пожалуйста, проголосуйте за меня как за босса :)
Мэтью Джеймс Дэвис,
да, я плохо делаю ... Вы проверили результат своей скрипки, он показывает ошибку .. match undefined
Balachandran
у меня отлично работает, в каком браузере вы работаете? это не поддерживается в старых версиях браузеров
Мэтью Джеймс Дэвис,
Эй, давай проясним этот отрицательный голос, что ты скажешь, братан
Мэтью Джеймс Дэвис,
3

Чтобы продлить ответ @SalmanPK

это позволит использовать узел в качестве селектора, что полезно при работе с такими событиями, как наведение мыши.

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 '
источник
2

Вот простая функция, которую я использую: -

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

Резюме:

Для поиска конкретного предка мы можем использовать:

Element.closest();

Эта функция принимает в качестве аргумента строку селектора CSS. Затем он возвращает ближайшего предка текущего элемента (или самого элемента), который соответствует селектору CSS, который был передан в аргументах. Если предка нет, он вернется null.

Пример:

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

Получите ближайший элемент DOM вверх по дереву, который содержит класс, идентификатор, атрибут данных или тег. Включает сам элемент. Поддерживается обратно в 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');
Крис Фердинанди
источник
Почему бы не использовать переключатель firstCharвместо всех IFусловий?
Rafael Herscovici
Зачем использовать скрытую forпетлю?
Rafael Herscovici
1

Найдите ближайшие элементы 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;

}

МаджидТахери
источник
0

Вот.

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

Находит только братьев и сестер дальше по дереву. Используйте previousSibling, чтобы пойти другим путем, или используйте переменные для обхода в обоих направлениях и возврата того, что будет найдено первым. Вы понимаете общую идею, но если вы хотите пройти через parentNodes или дочерние узлы, если родственник не соответствует, вы также можете использовать jQuery. На этом этапе это того стоит.

Ронни Ноксвилл
источник
0

Немного поздно на вечеринку, но когда я проходил мимо и просто ответил на очень похожий вопрос, я бросаю здесь свое решение - мы можем сказать, что это closest()подход JQuery , но на простом старом добром JavaScript.

Он не нуждается в каких-либо поллифилах, и это старые браузеры и дружественные IE (:-)): https://stackoverflow.com/a/48726873/2816279

Педро Феррейра
источник
-4

Я думаю, что самый простой код для улова с ближайшим 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>';
}
?>

Большое спасибо.

Аль МаМун
источник