Селектор соответствия ближайшего предка, использующий нативный DOM?

Кто-нибудь работает над jQuery.closest() эквивалентом в API DOM?

Похоже, добавлен черновик уровня 2 matches() эквивалентно jQuery.is (), поэтому родной ближайший должен быть намного легче писать. Имеет добавление closest() чтобы селекторы подошли?

6 ответов

Решение

Element.closest()

его поддержка

Реализация такой функции с Element.matches() кажется неоптимальной с точки зрения производительности, поскольку, очевидно, match () будет вызывать querySelectorAll() каждый раз, когда вы тестируете родительский элемент, в то время как для работы достаточно только одного вызова.

Вот polyfill для closest () в MDN. Обратите внимание на один вызов querySelectorAll()

if (window.Element && !Element.prototype.closest) {
  Element.prototype.closest = 
  function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
          i,
          el = this;
      do {
          i = matches.length;
          while (--i >= 0 && matches.item(i) !== el) {};
      } while ((i < 0) && (el = el.parentElement)); 
      return el;
  };
}

Но имейте в виду, что реализованная функция не будет работать должным образом на неприкрепленном дереве (отсоединено от корня document.documentElement)

//Element.prototype.closestTest = function(s){...as seen above...};

var detachedRoot = document.createElement("footer");
var child = detachedRoot.appendChild(document.createElement("div"));
detachedRoot.parentElement; //null

child.closestTest("footer"); //null

document.documentElement.append(detachedRoot);
child.closestTest("footer"); //<footer>   

Хотя closest (), реализованный в Firefox 51.0.1, кажется, работает нормально с отдельным деревом

document.documentElement.removeChild(detachedRoot);
child.closestTest("footer"); //null
child.closest("footer"); //<footer>

Построение ответа Алнитак. Вот рабочая текущая реализация с matchesSelector который сейчас matches в спецификации DOM.

// get nearest parent element matching selector
function closest(el, selector) {
    var matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;

    while (el) {
        if (matchesSelector.call(el, selector)) {
            break;
        }
        el = el.parentElement;
    }
    return el;
}

Поддержка браузера отличная: http://caniuse.com/matchesselector

Похоже, Chrome 40 принесет родной element.closest() метод ( http://blog.chromium.org/2014/12/chrome-40-beta-powerful-offline-and.html), указанный здесь: https://dom.spec.whatwg.org/

Это звучит так, как будто это должно быть довольно легко, учитывая matches функция, хотя это еще не поддерживается:

function closest(elem, selector) {
    while (elem) {
        if (elem.matches(selector)) {
            return elem;
        } else {
            elem = elem.parentElement;
        }
    }
    return null;
}

Проблема в том, что matches функция не поддерживается должным образом. Поскольку это все еще относительно новый API, он доступен как webkitMatchesSelector в Chrome и Safari, и mozMatchesSelector в Firefox.

Небольшая рекурсия сделает свое дело.

// get nearest parent element matching selector
var closest = (function() {
    var matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;

    return function closest(el, selector) {
        return !el ? null :
        matchesSelector.call(el, selector) ? el : closest(el.parentElement, selector);
    };
})();

Используя element.closest(), мы можем найти селектор соответствия ближайшего предка. Этот метод принимает список селекторов в качестве параметра и возвращает ближайшего предка. Согласно комментарию Роба, этот API будет доступен в Chrome 41 и FF 35.

Как объяснено в спецификации Whatwg https://dom.spec.whatwg.org/

Пример: в приведенном ниже HTML-коде появится предупреждение "true"

<html>
    <body>
        <foo>
            <bar>
                <a id="a">
                    <b id="b">
                        <c id="c"></c>
                    </b>
                </a>
            </bar>
         </foo>
    <script>
        var a = document.getElementById('a');
        var b = document.getElementById('b');
        var c = document.getElementById('c');
        alert(c.closest("a, b")==b);
    </script>
    </body>
</html>
Другие вопросы по тегам