Sam
Sam

Reputation: 657

CSS selection based on existence of sibling content

This as part of CSS selector learning I came across this problem. I am trying to select an element based on the existence of a sibling element. So If I have to quote an example,

<div class = "parentDiv">
  <div class = "abc">
    <span>Note</span>
  </div>
  <div class = "abd">
    <span><a>testLink</a></span>
  </div>
</div>
<div class = "parentDiv">
  <div class = "abc">
    <span>Note2</span>
  </div>
  <div class = "abd">
    <span>te</span>
  </div>
</div>

For the above example, I am trying to select all span elements inside class .abc if its sibling .abd has an a tag

What I tried is document.querySelectorAll('.parentDiv > .abc span ~ .abd span a'); which doesnt seem to work.. any idea what I could be doing wrong

DEMO

Upvotes: 2

Views: 237

Answers (3)

Okku
Okku

Reputation: 7819

This cannot be done with CSS selectors alone (yet).

The ~ selector selects elements on the same level in the DOM tree. Your a element isn't a direct sibling to your span, which is why your selector fails to return any results.

//What you'll want to do, is first select all instances of links:
var links = document.querySelectorAll('.parentDiv > .abd span a');

//Let's also create an empty array for matches:
var s = [];

//Then iterate over our matches:
Array.prototype.forEach.call(links, findSiblings);

function findSiblings(link){
    //Here, we can traverse back up to .parentDiv:
    var parent = link.parentElement.parentElement.parentElement;
    //Now, we have a only '.parentDiv's that contain '>.abd span a'. 
    //Let's run another query on that element to find any spans contained in '.abc':
    var spans = parent.querySelectorAll('.abc span');
    //Now, we have a new collection. Let's push each to our array:
    Array.prototype.forEach.call(spans, function(element){
        s.push(element)
    });
}

//Now, we have all the matches in our s-array:
alert(s.length);

Demo + a more compact version of the code: https://jsfiddle.net/p6nz011p/6/


Or, if you're using jQuery, you could simply do:

var s = $('.parentDiv:has(>.abd span a) >.abc span');
alert(s.length);

Note, that once browsers have support for the CSS Selectors Level 4 :has() selector, you may use this selector without jQuery in querySelectorAll(), too.

Upvotes: 1

Teocci
Teocci

Reputation: 8885

Update

It is an alternative using pure JavaScript:

var selectors = document.querySelectorAll('.abd span a');

if (selectors) {
  selectors.forEach(function(selector) {
    selector.style.backgroundColor = 'yellow';
    selector = selector.parentNode.parentNode.parentNode;
    elements = selector.querySelectorAll('.abc span');
    if (elements) {
      elements.forEach(function(element) {
        element.style.backgroundColor = 'red';
      });
      console.log('.abc span: ' + elements.length);
    }
  });

  console.log('.abd span a: ' + selectors.length);
}
<div class="parentDiv">
  <div class="abc">
    <span>Class> abc, tag> span: 1.0</span><br>
    <div>Class> abc, tag> div: 1.0</div>
    <span>Class> abc, tag> span: 1.1</span><br>
    <span>Class> abc, tag> span: 1.2</span><br>
    <div>Class> abc, tag> div: 1.1</div>
    <div>Class> abc, tag> div: 1.2</div>
  </div>
  <div class="abd">
    <span><a>Class> abd, tag> span a : 1.0 </a></span>
  </div>
</div>
<div class="parentDiv">
  <div class="abc">
    <span>Class> abc, tag> span: 2.0</span><br>
    <span>Class> abc, tag> span: 2.1</span><br>
    <span>Class> abc, tag> span: 1.2</span><br>
  </div>
  <div class="abd">
    <span>class> abd, tag> span: 2.0</span>
  </div>
  <div class="parentDiv">
    <div class="abc">
      <span>Class> abc: 3.0</span>
    </div>
    <div class="abd">
      <span><a>Class> abd, tag> span a : 3.0</a></span>
    </div>
  </div>

  <div class="parentDiv">
    <div class="abc">
      <span>Class> abc, tag> span: 4.0</span>
    </div>
    <div class="abd">
      <span>Class> abd, tag> span: 4.0</span>
    </div>
    <div class="abc">
      <span>Class> abc, tag> span: 4.1</span><br>
      <span>Class> abc, tag> span: 4.2</span><br>
      <span>Class> abc, tag> span: 4.3</span><br>
    </div>
  </div>

Upvotes: 1

itacode
itacode

Reputation: 3787

The tilde combinator selects only if the operands are childrens of the same parent. They must be at the same level. MDN reference.

Upvotes: 0

Related Questions