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

80
Vistas
Search DOM nodes and return the parent if any descendants contain a match

Let's say I have the following:

<div className="container">

  <div id="one" class="item">
    <div></div>
    <div></div>
    <div>
      <span>foo</span>
    </div>
  </div>

  <div id="two" class="item">
    <div></div>
    <div></div>
    <div>
      <div>foo</div>
    </div>
  </div>

  <div id="three" class="item">
    <span>foo</span>
    <div></div>
    <div>
      <span></span>
    </div>
  </div>

  <div id="four" class="item">
    <span></span>
    <div></div>
    <div>
      <div>
        <span>foo</span>
      </div>
    </div>
  </div>
  
</div>

I want to return all items that contain a span with text content "foo" anywhere within them. In the above example I would expect 1, 2, and 4 to be returned.

I've written a recursive method but am stumbling somewhere, I think in how I'm handling the return. This currently returns all items.

const hasFoo = (el) => {
  const isSpan = el.tagName?.toLowerCase() === "span";
  const isMatch = el.innerText?.toLowerCase() === "foo";

  if (isSpan && isMatch) {
    return true;
  } else if (el.children?.length) {
    const nodes = [...el.children];
    return nodes.forEach(node => hasFoo(node));
  } else {
    return false;
  }
};

const container = document.getElementsByClassName("container")[0];
const nodes = [...container.children];
const filtered = nodes.filter(node => hasFoo(node));
about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

A couple issues.

className is not a valid alternate for the class attribute. Because of that, document.getElementsByClassName("container") returns an empty array.

return nodes.forEach(node => hasFoo(node)) will ALWAYS return undefined. According to MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)

forEach() executes the callbackFn function once for each array element; unlike map() or reduce() it always returns the value undefined and is not chainable.

Use a for loop instead

const hasFoo = (el) => {
  const isSpan = el.tagName?.toLowerCase() === "span";
  const isMatch = el.innerText?.toLowerCase() === "foo";
  if (isSpan && isMatch) {
    return true;
  }
  const nodes = [...el.children];
  for (let i = 0; i < nodes.length; i++) {
    if (hasFoo(nodes[i]))
      return true;
  }
  return false;
}
about 4 years ago · Juan Pablo Isaza Denunciar

0

Others have pointed out the problems with className and your forEach. But for better experience that an explicit loop, use Array.prototype.some, like this:

const hasFoo = (el) => 
  (el .tagName ?.toLowerCase() === "span" && el .textContent ?.toLowerCase() === "foo") ||
  [...el .children ?? []] .some (hasFoo)

const foos = [... document .querySelectorAll (".container > *")] .filter (hasFoo)

foos .forEach (div => div .classList .add ('hasFoo'))
div {border: 1px solid #ccc; margin: .125em; padding: .125em;}
span {border: 1px solid #000; padding: .125em;}
div.hasFoo {background-color: yellow; border: 2px dotted #00f;}
<div class="container"><div id="one" class="item"><div></div><div></div><div><span>foo</span></div></div><div id="two" class="item"><div></div><div></div><div><div>foo</div></div></div><div id="three" class="item"><span>foo</span><div></div><div><span></span></div></div><div id="four" class="item"><span></span><div></div><div><div><span>foo</span></div></div></div></div>

Here we mark all <div>s with a light border, all <span>s with a darker one and highlight all the children of .container which have a foo-span with a yellow background and a dotted border.

about 4 years ago · Juan Pablo Isaza Denunciar

0

It's not recursive, but I prefer to return a filtered array of items based on the condition/requirement.

const items = Array.from(document.querySelectorAll('.item'));
const itemsWithFooSpans = items.filter(item => {
    let hasFoo = false;
    const spans = item.querySelectorAll('span');
    spans.forEach(span => {
        console.log('span: ', span);
        if (span && span.textContent && span.textContent === 'foo') {
            hasFoo = true;
        }
    });
    return hasFoo;
});
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