HTML JavaScript querySelector to find an element whose child has a specific data attribute

Viewed 1938

In my below HTML markup, I'd like to query the <div> that has a data-parent set to "true", and the contained child has data-child-gender set to "true" and inner html is "male".

    <div id="grandparent">
      <div id="parent1" data-parent="true">
        <div id="child1" data-child-gender="false">
          male
        </div>
      </div>
      <div id="parent2" data-parent="true">
        <div id="child2" data-child-gender="true">
          female
        </div>
      </div>
      <div id="parent3" data-parent="false">
        <div id="child3" data-child-gender="true">
          female
        </div>
      </div>
      <div id="parent4" data-parent="true">
        <div id="child4" data-child-gender="true">
          male
        </div>
      </div>
    </div>

Given the above scenario, the expected <div> is parent4.

What is the JavaScript querySelector to use?

3 Answers

First use querySelectorAll which will give an array. Then iterate over it and check and get element with required data attribute.

After that you can use use a if & check the content inside it

let k = document.querySelectorAll('[ data-parent=true]').forEach(function(item) {
  let elem = item.querySelector('[data-child-gender=true]');
  if (elem !== null && elem.innerHTML.trim() === 'male') {
    console.log(item.id)
  }
})
<div id="grandparent">
  <div id="parent1" data-parent="true">
    <div id="child1" data-child-gender="false">
      male
    </div>
  </div>
  <div id="parent2" data-parent="true">
    <div id="child2" data-child-gender="true">
      female
    </div>
  </div>
  <div id="parent3" data-parent="false">
    <div id="child3" data-child-gender="true">
      female
    </div>
  </div>
  <div id="parent4" data-parent="true">
    <div id="child4" data-child-gender="true">
      male
    </div>
  </div>
</div>

There isn't one querySelector you can use for this (as you can't use it to select specific text within elements). However, you can use .querySelector() with .filter() to get more specific results:

const true_children = [...document.querySelectorAll("[data-parent='true'] [data-child-gender='true']")];
const res = true_children.filter(({innerHTML:g}) => g.trim() === "male");

console.log(res);
<div id="grandparent">
  <div id="parent1" data-parent="true">
    <div id="child1" data-child-gender="false">
      male
    </div>
  </div>
  <div id="parent2" data-parent="true">
    <div id="child2" data-child-gender="true">
      female
    </div>
  </div>
  <div id="parent3" data-parent="false">
    <div id="child3" data-child-gender="true">
      female
    </div>
  </div>
  <div id="parent4" data-parent="true">
    <div id="child4" data-child-gender="true">
      male
    </div>
  </div>
</div>

The problem that the question describes, cannot be solved using query-selectors alone. This is because of following reasons:

  1. The query selectors always works on descendants, so while evaluating that the child div has data-child-gender="true", there will be no way to return the parent element. The query-selector will return the child div.
  2. There is no way to evaluate the inner text or contained text of an element in query-selector.

These two limitations can be worked around by using JavaScript, provided that you were going to use the query-selector in JS.

Something like the following snippet should work.

document.querySelectorAll('div[data-parent=true] div[data-child-gender=true]')
.filter(function(elem) {
  return elem.innerText === 'male'; // filter the elements containing male string.
})[0].parentElement; // return the parent of matched element.

An equivalent logic could be derived for selenium too. Otherwise if this much logic is unacceptable, you can always use the much richer xpath selectors. xpath wouldn't have either of the limitations mentioned above.

Related