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


90

Я намагаюся знайти найближчий елемент з конкретним ім'ям тегу без jquery. Коли я натискаю на a, <th>я хочу отримати доступ до <tbody>для цієї таблиці. Пропозиції? Я читав про офсет, але насправді не надто його розумів. Чи слід просто використовувати:

Припустимо, для th вже встановлено клацаний елемент th

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

1
Якщо ви виявите, що вам потрібно розпочати обхід через DOM, це один приклад, де зайві kbs, віднесені до jquery, того варті.
Kevin Bowersox


2
Я думаю, що це дуже важливе і слушне питання. Немає причин для проти.

el.closest('tbody')для не-тобто браузерів. Дивіться докладніше розроблену відповідь + поліфіл нижче.
оріадам

Відповіді:


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

124

Дуже просто:

el.closest('tbody')

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

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

Polyfill для 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
Чому ця відповідь знаходиться внизу сторінки? Має бути вгорі. Велике спасибі
Julien Le Coupanec

Найближче знаходиться в 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-> таблиця ніколи не враховує братів і сестер
hunterc

2
Ви повинні були вказати цей конкретний випадок у своєму питанні.
Joon

2
Дивись. Ця функція проходить батьківські вузли, щоб знайти найближчий батько (навіть за розширенням), який використовує наданий тег. Це не буде йти "вниз" по дереву документів, лише "вгору". Пересічний користувач бачив би "найближчий" вузол, швидше за все, як найближчого брата. Він просто не знає необхідної йому термінології.

1
Справедливості заради @Jhawins, що ваше визначення найближчого - це те, що jQuery означає найближчим. Це не питання jQuery. Я не можу підтвердити, чому jQuery вирішив, що найближчий означає найближчого предка, але більш розумним визначенням буде найближчим елементом, будь то батьки, попередні брати чи сестри, наступний брат та ін. Що б не було знайдено "найближчим" до цільового елемента.
ryandlf

1
@ryandlf Але що, якби батьки та брат чи сестра були на одній "відстані"? Визначення jQuery зрозуміло тим, що він поверне не більше одного збігу.
mtone

9

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

Щоб знайти найближчого tbodyбатька, якого 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");

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
Балачандран

приємний улов! виправлено, див. тут jsfiddle.net/c2pqc2x0 будь ласка, проголосуйте за мене як за боса :)
Метью Джеймс Девіс

ya i ill do ... Ви перевірили результат скрипки, він показує помилку .. матчі невизначені
Балачандран

для мене чудово працює, в якому браузері ви працюєте? це не підтримується у старих браузерах
Метью Джеймс Девіс

агов дозволяє з'ясувати , що downvote то , що ви говорите , братан
Метью Джеймс Девіс

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

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умов?
Рафаель Герсковічі,

Навіщо використовувати затемнений forцикл?
Рафаель Герсковічі,

1

Знайдіть найближчі Elements 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, щоб піти іншим шляхом, або використовуйте змінні для обходу обох шляхів і повернення того, що знайдено першим. Ви отримуєте загальну ідею, але якщо ви хочете здійснити обхід батьківських вузлів або дітей, якщо брат чи сестра не збігаються, ви також можете використовувати jQuery. На той момент це легко того варте.


0

Трохи пізно на вечірку, але, коли я проходив повз і просто відповів на дуже подібне питання, я опускаю сюди своє рішення - ми можемо сказати, що це closest()підхід JQuery , але в простому хорошому старому JavaScript.

Йому не потрібні будь-які pollyfills і це старіші браузери, і 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>';
}
?>

Велике спасибі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.