0

Below is an example of the layout with the method I'm currently using. I'm thinking there must be a simpler way to select something like this.

$('div[aria-label=Story]:not(div[aria-label=Story] div[aria-label=Story])')
<div aria-label="Story">a
  <span></span>
  <p></p>
  <div>
    <div aria-label="Story">b
    </div>
  </div>
</div>
<div aria-label="Story">c
</div>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
riegersn
  • 2,819
  • 3
  • 17
  • 19
  • 1
    is it possible to give the inner story divs a different class in the html during render? – Calvin Alvin Apr 03 '17 at 16:28
  • Are you sure it works ? I have created a snippet, doesn't seem to work ... – vals Apr 03 '17 at 17:44
  • @vals: Either he's using jQuery, or [he's testing in Safari](http://stackoverflow.com/questions/35993727/not-selector-not-behaving-the-same-between-safari-and-chrome-firefox). – BoltClock Apr 04 '17 at 09:33
  • @BoltClock I assumed this wasn't jQuery, based on the tags. Looking at the original question, the OP said *method*, so may be I was wrong ... Interesting to know about this Safari feature, didn't know about it :-) – vals Apr 04 '17 at 20:09
  • Yes actually I am using jQuery. Sorry I should have mentioned that. Will update the post. – riegersn Apr 06 '17 at 16:16
  • Was about to suggest changing the markup when I realised @CalvinAlvin already did – Chris Browne Apr 10 '17 at 12:15

2 Answers2

1

You can remove at least one of the duplicates and replace it with *, since the subject of the negation is already accounted for by the div[aria-label=Story] that's outside the negation:

$('div[aria-label=Story]:not(div[aria-label=Story] *)')

... but you will still need to specify the ancestor with another div[aria-label=Story] since there is no way to have a compound selector reference another in a complex selector without duplicating the compound selector you want referenced...

... unless you want to take things one step further and abstract the selector string itself, by storing the div[aria-label=Story] compound selector in a variable and interpolating it as appropriate:

var sel = 'div[aria-label=Story]';
$(sel + ':not(' + sel + ' *)');
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
0

Javascript selection is more sophisticated than CSS selection, so one approach is to use javascript to cycle through all the [aria-label="Story"] elements on the page, checking to see if each one contains at least one [aria-label="Story"] child element.

If it doesn't contain any, a class .contains-no-story may be applied which indicates that this element is an instance of [aria-label="Story"] which doesn't contain another of itself.

Working Example:

var stories = [... document.querySelectorAll('div[aria-label="Story"]')];

stories.forEach(function(story){
    story.classList.add('contains-no-story');
    for (var i = 0; i < story.children.length; i++) {
        if (stories.indexOf(story.children[i]) > -1) {
            story.classList.remove('contains-no-story');
        }
    }
});
[aria-label="Story"] {
  margin: 12px;
  padding: 12px;
  color: rgb(255, 255, 255);
  background-color: rgb(255, 0, 0);
}

.contains-no-story {
  background-color: rgb(0, 0, 255);
}
<div aria-label="Story">I contain another <code>[aria-label="Story"]</code> element
<div aria-label="Story">I do not contain another <code>[aria-label="Story"]</code> element</div>
</div>

<div aria-label="Story">I do not contain another <code>[aria-label="Story"]</code> element</div>

N.B. It's beyond the scope of this answer, but if you were to use axe selectors, then the CSS styles (including the any ancestor axe selector ^) would simply be:

[aria-label="Story"] {
background-color: rgb(0, 0, 255);
}

[aria-label="Story"] ^ [aria-label="Story"] {
background-color: rgb(255, 0, 0);
}

See: http://rounin.co.uk/projects/axe/axe2.html

Rounin
  • 27,134
  • 9
  • 83
  • 108