Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

88
Vistas
Unexpected results using Element.querySelector

Given the following markup (I've only included the relevant part for the sake of brevity)

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

Let's say that I have a reference to the li element with the ID of 'item' which I could get like this:

let item = document.querySelector('#item');

Now I need to retrieve the last anchor element within the nested ul list. For this I'm going to use the 'last-child' selector. This is what I initially tried:

let lastLink = item.querySelector('li:last-child > a');

I thought this would do the trick, but alas it actually returns the very first link in the markup ('Stuff'). I don't quite understand this as my base-element is already the 'item' element and I'm asking it to find the li element within that base-element which is the last-child of its parent and then return the a element from within it.

Indeed, if I do this:

let lastLink = item.querySelector('li:last-child');

It does actually return the last li element within the nested ul list, so why doesn't the first option work to simply get the 'a' element within that?

Here are some other things I tried which do work:

let lastLink = item.querySelector('#item li:last-child > a');

However, since I already have a reference to the base element it seems pointless to specify its ID here. These also work and return the correct a element:

let lastLink = item.querySelector('li ul li:last-child > a');
let lastLink = item.querySelector('li:last-child li:last-child > a');  

My question is, why doesn't the 1st option work? Appreciate if anyone can throw some light on the reason

about 4 years ago · Juan Pablo Isaza
1 Respuestas
Responde la pregunta

0

Doing rootElm.querySelector(selector) essentially does:

for (const node of recursiveDescendantNodes) {
  if (node.matches(selector)) {
    return node;
  }
}

The selector is not broken down and analyzed to see if the first part of the selector starts lower than the root node - usually, all that specifying the root node does is indicate which descendants to search through.

Since the first <a> that points to #foo matches the selector passed, it's returned.

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>

There is a way to get around that behavior, though, by using :scope - which, in supported browsers, indicates "the element querySelector (or querySelectorAll) was called on", which is the functionality you're hoping to see.

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>

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda