4

I am trying to replicate this jQuery selector using querySelector:

$("*:not(#player > div)")

I know I can select all the elements using querySelector('*'), but how do I exclude the elements matching #player > div

carte
  • 1,033
  • 2
  • 13
  • 29
  • You can use all CSS selectors that your browser supports in `querySelector`, so if your browser supports `:not` you're good to go. – m90 Jul 22 '15 at 19:24
  • 2
    But, `:not(#id > child)` is not supported – adeneo Jul 22 '15 at 19:26
  • 1
    Which probably means you have to iterate and filter, but you generally shouldn't be using the asterisk selector at all, so there's probably a better way to solve this. – adeneo Jul 22 '15 at 19:28

2 Answers2

3

Most jQuery selectors can be ported to querySelector() or CSS, but not all of them, as jQuery extends certain existing ones, along with introducing entirely non-standard selectors altogether.

In your particular case, unlike in jQuery, :not() only accepts a simple selector in CSS. #player > div is a complex selector, consisting of two simple selectors and a combinator. See this question for details:

Why is my jQuery :not() selector not working in CSS?

There is no standardized equivalent for your jQuery selector. You will have to exclude #player > div elements separately, by doing document.querySelectorAll('#player > div'), and removing elements matching that set from your original set of elements.

... well, actually, while there isn't a way to express :not(#player > div) in a single complex selector, because of the > combinator it is still possible to enumerate all the possibilities using no fewer than four complex selectors:

:root, :not(#player) > div, #player > :not(div), :not(#player) > :not(div)

Here's a proof-of-concept:

// The following JS + CSS should give all matching elements a 1px solid red border
$(':not(#player > div)').css('border-color', 'red');
* { margin: 10px; }
html::before { content: 'html - Match'; }
body::before { content: 'body - Match'; }

:root, :not(#player) > div, #player > :not(div), :not(#player) > :not(div) {
  border-style: solid;
  border-width: 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>body > p - Match</p>
<div>body > div - Match</div>
<div>
  body > div:not(#player) - Match
  <p>:not(#player) > p - Match</p>
  <div>:not(#player) > div - Match</div>
</div>
<div id=player>
  body > div#player - Match
  <p>#player > p - Match</p>
  <div>
    #player > div - Non-match
    <div>#player > div:not(#player) > div - Match</div>
  </div>
</div>

But you're still much better off either selecting those elements and removing them from your original set as described above, or just grabbing a matches() polyfill and excluding elements that way.

Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
0

Use this to remove the unwanted elements.

var el = Array.prototype.slice.call(document.querySelectorAll('*'),0)
el.splice(el.indexOf(document.querySelectorAll('html')),1)
Imesh Chandrasiri
  • 5,558
  • 15
  • 60
  • 103
  • I don't think that this will ever work... `Array#indexOf` can only handle one element to locate in the array. – yckart Mar 22 '16 at 18:55