2013-03-10 5 views
14

Кто-нибудь работает над эквивалентом jQuery.closest() в DOM api?Самый близкий селектор соответствия предков с использованием встроенного DOM?

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

+0

Это «должно быть проще», только если все браузеры реализуют метод - их нет.:-(Так что метод longhand должен быть написан так или иначе, я думаю, с обнаружением функции для вилки 'macthes'. Это было бы не сложно, но, вероятно, немного медленнее. – RobG

+0

Да, [у модернизатора есть пример] (http: /modernizr.com/docs/#prefixeddom) отпадает назад к поставщику с префиксом 'matchesSelector'. – hurrymaplelad

ответ

3

Element.closest()

its support

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

Вот полипол для ближайшего() на 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> 

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

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

Кажется, что больше не «экспериментальный» в соответствии с MDN: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest – Xenos

3

Это звучит, как это должно быть довольно легко, учитывая функции 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.

+0

Да, с' * MatchesSelector', взбираясь ближе всего, используя только родной DOM, становится легче. Мне более любопытно, есть ли какой-то импульс добавив 'element.closest (selector)' в качестве нативного метода элементов. – hurrymaplelad

+0

@hurrymaplelad вам, вероятно, придется присоединиться к W3C и предложить его. Я подозреваю, что они не потрудились, учитывая, насколько легко реализовать (на код выше). – Alnitak

33

Строительство ответа Алнитак. Вот текущая реализация с 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

+0

Я разорван. Спасибо за реализацию, но означает ли это, что родной ближайший не наступает? – hurrymaplelad

+1

Почему '.bind (ctx) (arg)', а не '.call (ctx, arg)'? Первый создает новый контекст выполнения, последний не делает. – Alnitak

+1

'call (elem, selector)' имеет лучшую производительность, чем 'bind (elem) (selector)', эталон http://jsperf.com/native-vs-jquery-closest. Еще простой метод '.parentNode.parentNode' выполняется очень быстро. – Binyamin

11

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

+2

'element.closest' приземлился в Chrome 41, а не 40 (см. Https://crbug.com/422731#c7). И он также попал в [Firefox 35] (https://developer.mozilla.org/en-US/Firefox/Releases/35) –

+0

Вы также можете использовать полиполк, чтобы использовать эту функцию в старых браузерах, например https: //github.com/jonathantneal/closest – user1613797

1

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

Как объяснен в WHATWG спецификации https://dom.spec.whatwg.org/#dom-element-closest

Пример: ниже HTML появится предупреждающее сообщение «истинный»

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

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

// 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); 
    }; 
})(); 
+0

Или просто 'return element.tagName === 'HTML'? null: element.matches (селектор)? элемент: ближайший (element.parentNode, селектор); ';) – yckart

+0

@yckart обновлен. –