Dado el siguiente marcado (solo he incluido la parte relevante en aras de la brevedad)
<ul> <li id="item"> <a href="#foo">Stuff</a> <ul> <li> <a href="#foo">Foo stuff</a></li> <li><a href="#bar">Bar Stuff</a></li> <li><a href="#foobar">Foo-Bar Stuff</a></li> </ul> </li> </ul>Digamos que tengo una referencia al elemento li con el ID de 'elemento' que podría obtener así:
let item = document.querySelector('#item');Ahora necesito recuperar el último elemento ancla dentro de la lista ul anidada. Para esto voy a usar el selector de 'último hijo'. Esto es lo que probé inicialmente:
let lastLink = item.querySelector('li:last-child > a');Pensé que esto funcionaría, pero, por desgracia, en realidad devuelve el primer enlace en el marcado ('Cosas'). No entiendo bien esto ya que mi elemento base ya es el elemento 'elemento' y le pido que encuentre el elemento li dentro de ese elemento base que es el último hijo de su padre y luego devuelva el elemento a desde dentro de ella.
De hecho, si hago esto:
let lastLink = item.querySelector('li:last-child');En realidad , devuelve el último elemento li dentro de la lista ul anidada, entonces, ¿por qué la primera opción no funciona para simplemente obtener el elemento 'a' dentro de eso?
Aquí hay algunas otras cosas que probé que funcionan:
let lastLink = item.querySelector('#item li:last-child > a');Sin embargo, dado que ya tengo una referencia al elemento base, parece inútil especificar su ID aquí. Estos también funcionan y devuelven el elemento correcto:
let lastLink = item.querySelector('li ul li:last-child > a'); let lastLink = item.querySelector('li:last-child li:last-child > a');Mi pregunta es, ¿por qué no funciona la primera opción? Agradezco si alguien puede arrojar algo de luz sobre la razón.
Hacer rootElm.querySelector(selector) esencialmente hace:
for (const node of recursiveDescendantNodes) { if (node.matches(selector)) { return node; } } El selector no se desglosa ni se analiza para ver si la primera parte del selector comienza más abajo que el nodo raíz; por lo general, todo lo que hace la especificación del nodo raíz es indicar qué descendientes buscar.
Dado que el primer <a> que apunta a #foo coincide con el selector pasado, se devuelve.
console.log( document.querySelector('#item > a').matches('li:last-child > a') ); <ul> <li id="item"> <a href="#foo">Stuff</a> <ul> <li> <a href="#foo">Foo stuff</a></li> <li><a href="#bar">Bar Stuff</a></li> <li><a href="#foobar">Foo-Bar Stuff</a></li> </ul> </li> </ul> Sin embargo, hay una manera de sortear ese comportamiento mediante el uso de :scope , que, en los navegadores compatibles, indica que "se invocó el elemento querySelector (o querySelectorAll )", que es la funcionalidad que espera ver.
let item = document.querySelector('#item'); let lastLink = item.querySelector(':scope li:last-child > a'); console.log(lastLink); <ul> <li id="item"> <a href="#foo">Stuff</a> <ul> <li> <a href="#foo">Foo stuff</a></li> <li><a href="#bar">Bar Stuff</a></li> <li><a href="#foobar">Foo-Bar Stuff</a></li> </ul> </li> </ul>