I was recently unable to find simple, basic examples of using :not() in a JavaScript selector. As always, maybe I missed something, and I hate contributing to the noise out there, but here’s a summary of simple, basic usage—so simple I won’t even show any HTML.
First of all, the kind of selector I’m talking about is used in calls such as Element.querySelectorAll().
To select all elements that have classes x, y, and z:
.x.y.z
To select all elements that have classes x, y, or z:
.x, .y, .z
To select all elements that have class x but not class y or class z:
.x:not(.y):not(.z)
To select all elements that aren’t DIV elements:
:not(div)
To select all DIV elements that don’t have class x:
div:not(.x)
To select all DIV elements that don’t have class x but do have class y:
div:not(.x).y
Note that the following is a syntax error:
:not(div.x)
:not() takes a simple selector. A simple selector is a single element type, class, attribute, id, or pseudo class. In retrospect, this is obvious, as negating a multi-term AND expression gets mind-bendingly weird.
To select all elements that are not DIVs and not of class x:
:not(div):not(.x)
Words of warning
If you misspell part of a :not() selector, chances are it will match all elements in the document. You might be used to spelling errors usually resulting in not matching anything.
Using :not() in a selector might result in more matches than expected and if acting as ancestors might not serve to reduce the final selection at all. See the next example.
An example involving ancestors
Here is a nonparallel case that plays tricks with the mind, involving ancestors.
To select all y elements that have an ancestor x element:
.x .y
I think that it is impossible to write a complementary selector that selects all y elements that do not have an ancestor x element. The following does not work:
:not(.x) .y
If a y element descends from any element without the x class, it will match. This is (essentially) always all y elements. You end up needing to write code that keeps only the y elements that don’t have an x element as an ancestor. For example:
Array.from(document.querySelectorAll(“.y”)).filter(item => !item.parentNode.closest(“.x”));
References:
Here’s another attempt at explaining the differences in approaches:
- Selector: Even y elements that do have an x ancestor (i.e., should be filtered out) will also have another ancestor that is not an x element (i.e., is not filtered). So no filtering takes place; you get all y elements.
- Code: There will always be an ancestor without x; you want to know if x cannot be found among all ancestors, not any of them.
On the other hand, if you know and can have a dependency on the precise HTML structure, and you know (e.g.) the x element is a direct parent of the y element (rather than an arbitrary ancestor), you can use this:
:not(.x) > .y
This selects all y elements that don’t have a parent x.
An example involving descendants
Maybe I’m going crazy, but descendants get even weirder. You cannot use selectors with or without a :not() .
Find all x elements that do contain another element of type y :
Array.from(document.querySelectorAll(“.x”)).filter(item => !!item.querySelector(“.y”));
Find all x elements that don’t contain another element of type y (the only difference is the not [!]):
Array.from(document.querySelectorAll(“.x”)).filter(item => !item.querySelector(“.y”));
Leave a Reply