2013-03-10 9 views
14

Arbeitet jemand an einem jQuery.closest() Äquivalent in der DOM-API?Der engste Vorgänger für den Vorfahrenvergleich mit nativem DOM?

Sieht aus wie die Selectors Level 2 draft fügt matches() entspricht jQuery.is(), so native am nächsten sollte viel einfacher zu schreiben sein. Hat das Hinzufügen von closest() zu Selektoren kommen?

+0

Es funktioniert "sollte einfacher sein" nur, wenn alle Browser implementieren die Methode-sie nicht.:-(Also eine Longhand-Methode muss sowieso geschrieben werden, ich denke mit Feature-Erkennung für eine Gabel "macthes". Es wäre nicht schwer, aber wahrscheinlich ein wenig langsam. – RobG

+0

Ja, [modernisierer hat ein Beispiel] (http://modernizr.com/docs/#prefixeddom) des Zurückfallens auf den Hersteller 'matchesSelector' – hurrymaplelad

Antwort

3

Element.closest()

its support

Die Umsetzung einer solchen Funktion mit Element.matches() scheint nicht optimal in Bezug auf Leistung, führen offenbar übereinstimmt() wird jedes Mal, wenn Sie einen Anruf zu querySelectorAll() machen Testen Sie einen Elternteil, während nur ein Anruf für den Job ausreicht.

Hier ist ein Polyfill für engsten() auf MDN. Hinweis einen einzigen Anruf zu 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; 
    }; 
} 

Aber beachten Sie, dass wie diese implementierte Funktion nicht richtig auf nicht befestigten Baum arbeiten (losgelöst von document.documentElement root)

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

Obwohl am nächsten() das ist in Firefox 51.0.1 implementiert scheint gut mit freistehenden Baum

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

Scheint nicht mehr "experimentell" nach MDN zu sein: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest – Xenos

3

Das klingt wie es sollte ziemlich einfach sein, angesichts der matches Funktion, obwohl das nicht weit noch unterstützt wird:

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

Das Problem ist, die matches Funktion nicht richtig unterstützt wird. Da es immer noch eine relativ neue API ist, ist es in Chrome und Safari als webkitMatchesSelector und in Firefox als mozMatchesSelector verfügbar.

+0

Ja, mit' * MatchesSelector' wird das Aufrüsten auf ein natives DOM einfach, ich bin eher neugierig, ob es einen Schwung gibt Hinzufügen von 'element.closest (selector)' als eine native Methode von Elementen – hurrymaplelad

+0

@hurrymplelad würden Sie wahrscheinlich W3C beitreten und es vorschlagen.Ich vermute, dass sie nicht stören würde, wenn man bedenkt, wie einfach es ist, zu implementieren (per code oben) – Alnitak

33

Aufbau von Alnitaks Antwort. Hier ist die Arbeit aktuelle Implementierung mit matchesSelector die jetzt matches in der DOM-Spezifikation ist.

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

Browser-Unterstützung ist groß: http://caniuse.com/matchesselector

+0

Ich bin zerrissen.Danke für die Implementierung, aber bedeutet es, dass native engste nicht kommt? – hurrymaplelad

+1

Warum '.bind (ctx) (arg)' anstatt '.call (ctx, arg)'? Der erste erzeugt einen neuen Ausführungskontext, der zweite keinen. – Alnitak

+1

'call (elem, selector)' hat eine bessere Leistung als 'bind (elem) (selector)', Benchmark http://jsperf.com/native-vs-jquery-closest. Die einfache '.parentNode.parentNode' Methode ist sehr viel schneller. – Binyamin

11

Scheint, wie Chrome 40 wird eine native element.closest() Methode (http://blog.chromium.org/2014/12/chrome-40-beta-powerful-offline-and.html) angegeben bringen: https://dom.spec.whatwg.org/#dom-element-closest

+2

'element.closest' ist in Chrome 41 und nicht in 40 gelandet (siehe https://crbug.com/422731#c7). Und es ist auch gelandet in [Firefox 35] (https://developer.mozilla.org/en-US/Firefox/Releases/35) –

+0

Sie können auch ein Polyfill verwenden, um diese Funktion in älteren Browsern zu verwenden, zum Beispiel https: //github.com/jonathantneal/closest – user1613797

1

Mit element.closest() wir Closest Vorfahren passende finden Wähler. Diese Methode verwendet die Selektorenliste als Parameter und gibt den nächstliegenden Vorgänger zurück. Wie pro Robs Kommentar wird diese API aus Chrom 41 und FF zur Verfügung 35.

Wie in WHATWG Spezifikationen erklärt https://dom.spec.whatwg.org/#dom-element-closest

Beispiel: Die unten HTML wird Warnmeldung anzeigen "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> 
1

A kleine Rekursion wird den Trick machen.

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

Oder einfach 'return element.tagName === 'HTML'? null: element.matches (Selektor)? element: engste (element.parentNode, Selektor); ';) – yckart

+0

@yckart aktualisiert. –

Verwandte Themen