Селектор соответствия ближайшего предка, использующий нативный DOM?
Кто-нибудь работает над jQuery.closest() эквивалентом в API DOM?
Похоже, добавлен черновик уровня 2 matches()
эквивалентно jQuery.is (), поэтому родной ближайший должен быть намного легче писать. Имеет добавление closest()
чтобы селекторы подошли?
6 ответов
Реализация такой функции с 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>