queryselectorall queryselector query none from ejemplos data attribute javascript css-selectors

none - queryselector javascript ejemplos



Prueba si un selector coincide con un elemento dado (7)

¿Hay alguna manera de probar si un selector coincidiría con un elemento DOM determinado? Preferiblemente, sin el uso de una biblioteca externa como Sizzle. Esto es para una biblioteca y me gustaría minimizar la cantidad de complementos de terceros requeridos para la biblioteca "core". Si termina requiriendo Sizzle, lo agregaré como un complemento a la biblioteca para aquellos que quieran la característica que habilitaría.

Por ejemplo, podría hacer algo como:

var element = <input name="el" /> matches("input[name=el]", element) == true

EDITAR : Después de pensarlo más, se me ocurrió una solución, esto técnicamente funciona, pero no parece óptimo en términos de eficiencia:

function matchesSelector(selector, element) { var nodeList = document.querySelectorAll(selector); for ( var e in nodeList ) { return nodeList[e] === element; } return false; }

Básicamente, la función consulta el documento completo con el selector dado y luego itera sobre nodeList. Si el elemento dado está en nodeList, devuelve true, y si no lo está, devolverá false.

Si alguien puede llegar a una respuesta más eficiente, con mucho gusto marcaría su respuesta como la respuesta.

EDITAR : Flavius ​​Stef me señaló una solución específica para navegador para Firefox 3.6+, mozMatchesSelector . También encontré el equivalente para Chrome (la compatibilidad de la versión es desconocida, y puede que funcione o no en Safari u otros navegadores webkit): webkitMatchesSelector , que es básicamente lo mismo que la implementación de Firefox. Todavía no he encontrado ninguna implementación nativa para los navegadores IE.

Para el ejemplo anterior, el uso sería:

element.(moz|webkit)MatchesSelector("input[name=el]")

Parece que el W3C también ha abordado esto en la especificación de nivel II de la API de selectores (todavía un borrador en este momento). matchesSelector será un método en los elementos DOM una vez aprobado.

Uso de W3C: element.matchesSelector(selector)

Dado que esa especificación todavía es un borrador y hay un tiempo de retraso antes de que los navegadores populares implementen los métodos una vez que se convierta en el estándar, puede pasar un tiempo hasta que esto realmente se pueda utilizar. La buena noticia es que si utiliza alguno de los marcos populares, es probable que implementen esta funcionalidad sin tener que preocuparse por la compatibilidad con navegadores cruzados. Aunque eso no ayuda a aquellos de nosotros que no podemos incluir bibliotecas de terceros.

Marcos o bibliotecas que implementan esta funcionalidad:

http://www.prototypejs.org/api/element/match

http://developer.yahoo.com/yui/docs/YAHOO.util.Selector.html

http://docs.jquery.com/Traversing/is

http://extjs.com/deploy/dev/docs/output/Ext.DomQuery.html#Ext.DomQuery-methods

http://base2.googlecode.com/svn/doc/base2.html#/doc/!base2.DOM.Element.matchesSelector

http://wiki.github.com/jeresig/sizzle/


En ausencia de xMatchesSelector , estoy pensando en intentar agregar un estilo con el selector solicitado a un objeto styleSheet , junto con alguna regla y valor arbitrario que probablemente ya no esté en uso. Luego, compruebe el estilo computed/currentStyle del elemento para ver si ha heredado la regla CSS añadida. Algo como esto para IE:

function ieMatchesSelector(selector, element) { var styleSheet = document.styleSheets[document.styleSheets.length-1]; //arbitrary value, probably should first check //on the off chance that it is already in use var expected = 91929; styleSheet.addRule(selector, ''z-index: ''+expected+'' !important;'', -1); var result = element.currentStyle.zIndex == expected; styleSheet.removeRule(styleSheet.rules.length-1); return result; }

Probablemente haya un bolso lleno de gotcha con este método. Probablemente sea mejor encontrar alguna regla CSS oculta desconocida que tenga menos posibilidades de tener un efecto visual que z-index , pero dado que se elimina casi inmediatamente después de establecerla, un breve parpadeo debería ser el único efecto secundario. También es menos probable que una regla más oscura sea anulada por un selector más específico, reglas de atributos de estilo u otras reglas importantes (si IE incluso lo admite). De todos modos, vale la pena intentarlo al menos.


Estoy lidiando con este problema ahora. Tengo que admitir IE8 con Javascript nativo, lo que presenta un curioso desafío: IE8 admite tanto querySelector como querySelectorAll, pero no coincide conSelector. Si su situación es similar, aquí hay una opción para que considere:

Cuando le entreguen el nodo DOM y un selector, haga una copia superficial del nodo y de su padre. Esto conservará todos sus atributos pero no hará copias de sus respectivos hijos.

Adjunte el nodo clonado al padre clonado. Use querySelector en el padre clonado: lo único que necesita buscar es el único nodo hijo que tiene, por lo que este proceso es constante. Devolverá el nodo hijo o no lo hará.

Eso se vería así:

function matchesSelector(node, selector) { var dummyNode = node.cloneNode(false); var dummyParent = node.parent.cloneNode(false); dummyParent.appendChild(dummyNode); return dummyNode === dummyParent.querySelector(selector); }

Puede valer la pena crear una cadena completa de padres con copia poco profunda hasta el nodo raíz y consultar la raíz ficticia (en su mayoría vacía) si desea poder probar la relación de su nodo con sus antepasados.

No estoy seguro de para qué parte de los selectores funcionaría esto, pero creo que sería bueno para cualquiera que no se preocupara por los hijos del nodo evaluado. YMMV.

- EDITAR -

Decidí escribir la función para copiar todo desde el nodo que se está probando hasta la raíz. Usando esto, se pueden emplear muchos más selectores. (Nada relacionado con hermanos, sin embargo)

function clonedToRoot(node) { dummyNode = node.cloneNode(false); if(node.parentNode === document) { return {''root'' : dummyNode, ''leaf'' : dummyNode}; } parent = clonedToRoot(node.parentNode).root; parent.appendChild(dummyNode); return {''root'' : parent, ''leaf'' : dummyNode}; } function matchesSelector(node, selector) { testTree = clonedToRoot(node) return testTree.leaf === testTree.root.querySelector(selector) }

¡Agradecería a un experto que me explicara qué tipos de selectores hay que esto no cubriría!




Para beneficio de los que visitan esta página después de estos muchos años, esta funcionalidad ahora se implementa en todos los navegadores modernos como element.matches sin prefijo de proveedor (excepto para MS para navegadores MS distintos de Edge 15 y webkit para Android / KitKat). Ver http://caniuse.com/matchesselector .


Para un mejor rendimiento , use las implementaciones del navegador ( (moz|webkit|o|ms)matchesSelector ) cuando sea posible. Cuando no puede hacer eso, aquí hay una implementación manual.

Un caso importante a considerar es probar los selectores para los elementos que no están adjuntos al documento.

Aquí hay un enfoque que maneja esta situación. Si resulta que el element en cuestión no está adjunto al documento, rastree el árbol para encontrar el antecesor más alto (el último parentNode no nulo) y parentNode en un DocumentFragment . Luego, a partir de ese DocumentFragment querySelectorAll y vea si su element está en NodeList resultante.

Aquí está el código.

El documento

Aquí hay una estructura de documentos con la que trabajaremos. Tomaremos el .element y .element si coincide con los selectores li y .container * .

<!DOCTYPE html> <html> <body> <article class="container"> <section> <h1>Header 1</h1> <ul> <li>one</li> <li>two</li> <li>three</li> </ul> </section> <section> <h1>Header 2</h1> <ul> <li>one</li> <li>two</li> <li class="element">three</li> </ul> </section> <footer>Footer</footer> </article> </body> </html>

Búsqueda con document.querySelectorAll

Aquí hay una función matchesSelector que usa document.querySelectorAll .

// uses document.querySelectorAll function matchesSelector(selector, element) { var all = document.querySelectorAll(selector); for (var i = 0; i < all.length; i++) { if (all[i] === element) { return true; } } return false; }

Esto funciona siempre que ese elemento esté en el document .

// this works because the element is in the document console.log("Part 1"); var element = document.querySelector(".element"); console.log(matchesSelector("li", element)); // true console.log(matchesSelector(".container *", element)); // true

Sin embargo, falla si el elemento se elimina del document .

// but they don''t work if we remove the article from the document console.log("Part 2"); var article = document.querySelector("article"); article.parentNode.removeChild(article); console.log(matchesSelector("li", element)); // false console.log(matchesSelector(".container *", element)); // false

Buscando dentro de un DocumentFragment

La solución requiere buscar el subárbol en el que esté el element . Aquí hay una función actualizada llamada matchesSelector2 .

// uses a DocumentFragment if element is not attached to the document function matchesSelector2(selector, element) { if (document.contains(element)) { return matchesSelector(selector, element); } var node = element; var root = document.createDocumentFragment(); while (node.parentNode) { node = node.parentNode; } root.appendChild(node); var all = root.querySelectorAll(selector); for (var i = 0; i < all.length; i++) { if (all[i] === element) { root.removeChild(node); return true; } } root.removeChild(node); return false; }

Ahora vemos que matchesSelector2 funciona aunque el elemento se encuentre en un subárbol separado del documento.

// but they will work if we use matchesSelector2 console.log("Part 3"); console.log(matchesSelector2("li", element)); // true console.log(matchesSelector2(".container *", element)); // true

Puedes ver esto trabajando en jsfiddle .

Poniendolo todo junto

Aquí está la implementación final que surgió:

function is(element, selector) { var node = element; var result = false; var root, frag; // crawl up the tree while (node.parentNode) { node = node.parentNode; } // root must be either a Document or a DocumentFragment if (node instanceof Document || node instanceof DocumentFragment) { root = node; } else { root = frag = document.createDocumentFragment(); frag.appendChild(node); } // see if selector matches var matches = root.querySelectorAll(selector); for (var i = 0; i < matches.length; i++) { if (this === matches.item(i)) { result = true; break; } } // detach from DocumentFragment and return result while (frag && frag.firstChild) { frag.removeChild(frag.firstChild); } return result; }

Una nota importante es que la implementación de jQuery es mucho más rápida. La primera optimización que consideraría es evitar gatear por el árbol si no es necesario. Para hacer esto, podría mirar la parte más a la derecha del selector y probar si esto coincide con el elemento. Sin embargo, tenga en cuenta que si el selector es en realidad múltiples selectores separados por comas, entonces tendrá que probar cada uno. En este momento está construyendo un analizador de selector de CSS, por lo que también podría usar una biblioteca.


Solo usa una identificación para tu elemento? Los ID de HTML deben ser únicos ...