Negative selecting without direct path in CSS [duplicate] - css

This question already has answers here:
CSS negation pseudo-class :not() for parent/ancestor elements
(2 answers)
Closed 5 years ago.
I want the following selector to match only those <span> elements that are direct children of '.grand-grand-child' and not descendants of '.grand-grand-parent':
:not(.grand-grand-parent) .grand-grand-child > span {
color: blue;
}
But it fails to apply the rule. Is it possible to solve the problem without Javascript? In my experience, :not rules at the beginning have to be followed with direct path made with > signs. Am I right?

See, there's a problem here: the first part of this selector will be applied to any element in the second selector's match ancestor chain (in attempt to match the whole rule). Consider the following:
:not(.parent) .child {
color: blue;
}
<div class="parent">
<div class="child">
Which color am I?
</div>
</div>
And the answer is blue, even though that .child element is clearly matched by .parent .child rule. The problem is, this rule reads as
match any element with class 'child' if one of its ancestors is without class 'parent'
And of course, it has such an ancestor - <body> element. Now compare with this fragment:
:not(.parent) > .child {
color: blue;
}
<div class="parent">
<div class="child">
Which color am I?
</div>
</div>
And now the answer is black, as the selector reads as...
match any element with class 'child' if its direct parent is without class 'parent'
Another way will be opened when browsers start supporting CSS Selectors Level 4 negation spec, allowing something more than simple selector as :not argument. It'll be possible to write something like:
.child:not(.parent *) { /* */ }
And now if any element is ancestor chain of .child matches .parent, it's not matched. But both Chrome and Firefox at the moment of writing still lack support of this feature - they only support CSS Level 3 negation.

Related

Two classes in :not() doesn't seem to work in Safari, but it works in Firefox and Chrome

I'm using this snippet and it works in Firefox and Chrome, but not in Safari. Why?
.column:not(.custom.no-edit)
If I change it to this it seems to work, but that doesn't give the same effect does it? If I'm thinking correctly the first one will give true if both classes are present while the second will give true if any of the classes are present?
.column:not(.custom):not(.no-edit)
Consider the following HTML fragment:
<div class="column"></div>
<div class="column custom"></div>
<div class="column no-edit"></div>
<div class="column custom no-edit"></div>
The selector
.column:not(.custom.no-edit)
is level 4 syntax that matches .column elements that don't have both "custom" and "no-edit" in their class attributes. This means that .column elements that have either one or the other will match. All of the first three elements in the fragment will match — only the fourth will not.
In level 3 implementations, the syntax is considered invalid and gets ignored, resulting in none of the elements in the fragment matching the selector.
The level 3 equivalent that's understood by Firefox and Chrome is
.column:not(.custom), .column:not(.no-edit)
The selector
.column:not(.custom):not(.no-edit)
is level 3 syntax that matches .column elements that don't have either "custom" or "no-edit" in their class attributes. Only the first element in the fragment will match this selector. The level 4 equivalent is
.column:not(.custom, .no-edit)
Hopefully this helps you understand the results you expect and the results you actually get.
See also:
Why is my jQuery :not() selector not working in CSS?
:not() selector not behaving the same between Safari and Chrome/Firefox
Are you sure .column:not(.custom.no-edit) worked in chrome and FF but not safari ?
That is a CSS level 4 syntax. However Safari is known to support it.
Let's take a look at the spec.
CSS level 3
The The negation pseudo-class, :not(X), is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument. It represents an element that is not represented by its argument.
A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
Something that is not mentioned in the spec, :not() does not accept compound Selectors
A compound selector is a sequence of simple selectors that are not separated by a combinator, and represents a set of simultaneous conditions on a single element.
Basically chaining classes and types etc.., Example:
.class1.class2.class3
#myId.MyClass
div.MyClass#yourId
CSS level 4
The The negation pseudo-class, :not(), is a functional pseudo-class taking a selector list as an argument. It represents an element that is not represented by its argument.
In CSS level 4 it will become free for all.
Is div:not(.a):not(.b) equal to div:not(.a.b)
div {
display: inline-block;
width: 80px;
height: 80px;
border: 4px solid black;
}
div:not(.a.b) {
background-color: green;
}
<h3>div { ... }: select all divs</h3>
<h3>.a.b { ... }: select all elements with both classes .a and.b </h3>
<h3>If we combine the two using :not() The first two boxes should be green</h3>
<div class="a"></div>
<div class="b"></div>
<div class="a b"></div>
If the above selector were to work it will select everything except those elements that have both classes a and b
Which means it is not equivalent to div:not(.a):not(.b) as this will ignore all elements that have both classes or either one, And we only want to ignore elements that have both.
Solution
We can make use of attribute selectors until css level 4 drops in, if you don't mind a bit of care.
div:not([class^="a b"])
This will select all div elements except those that begin with both classes a and b other classes can be applied to the element so styling will be normal.
Your classes must always be preceded by the string a b in order for this to work, same goes the opposite selector [attr$=value]
div {
display: inline-block;
width: 80px;
height: 80px;
border: 4px solid black;
}
div:not([class^="a b"]) {
background-color: green;
}
<div class="a"></div>
<div class="b"></div>
<div class="a b some other classes"></div>
Most browsers support attribute selectors, so support won't be a problem.

:not selector is changing the evaluated order of equivalent selectors [duplicate]

This question already has answers here:
What is the most character-efficient way to increase CSS specificity?
(4 answers)
Closed 4 years ago.
I have an issue where I'm working in a rather large CSS code base that makes frequent use of overriding previously defined classes/selectors. As such, it is very sensitive to the order in which they are defined.
Here is a sample of how I need it to work
.grid {
padding:25px;
background-color: red;
}
.grid {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
Notice how the second .grid definition overrides the first.
This is what is happening now:
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
Using the :not pseudo-class hover moves the evaluated priority to after normal class definitions. I need it to be evaluated in the same order as before, but I need the :not selector. Are there any solutions besides refactoring?
The :not rule is more specific, so it takes higher precedence.
If you can't refactor, you could put a bogus :not condition on the other rule as well, so they'll have the same precedence and thus revert to document order:
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid:not(.nonexistentclassname) {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
You just need to make the selector you want to take precedence be more specific than the other one. If you add a "dummy" class to the element, you can add that class to your second selector to make it more specific (or at least make a tie where then, the last selector wins).
CSS Specificity is calculated as follows:
1000 points for an inline style
100 points for an id in the selector
10 points for a class or pseudo-class in the selector
1 point for an element or pseudo-element in the selector
In your case:
.grid:not(.ui)
Is worth 20 points because the selector has 1 class and one pseudo-classes in it.
But:
.grid
is only worth 10 points because of the one class.
/* This selector is worth 20 points */
.grid:not(.ui) {
padding:25px;
background-color: red;
}
/* This selector is also worth 20 points, but becomes it comes
after the other one, the location breaks the tie. */
.grid.special {
padding:50px;
background-color: green;
}
<li>
<!-- Adding another "dummy" class to the element allows you
to correctly find it with your CSS, and do it with a more
specific selector, if needed. -->
<div class="grid special">
Test
</div>
</li>
And, what if you need (for some reason) have the order of the selectors reversed? Just make the one that is suppose to "win" a little more specific:
/* This selector is worth 21 points */
div.grid.special {
padding:50px;
background-color: green;
}
/* This selector is worth 20 points */
.grid:not(.ui) {
padding:25px;
background-color: red;
}
<li>
<!-- Adding another "dummy" class to the element allows you
to correctly find it with your CSS, and do it with a more
specific selector, if needed. -->
<div class="grid special">
Test
</div>
</li>
Here's a great site for understanding how specificity is calculated that let's you "play" with selectors.
In your first example the .grid selectors each have a specificity value of 10 (classes = 10). Therefore, since both rules have the same specificity, their source order decides.
In your second rule, .grid:not(.ui) has a specificity value of 20 (2 classses; the :not() pseudo-class has no specificity value). The source order is subordinate because the rules have different specificity values.
So, to achieve your goal (the same behavior as before but with :not() applied to the first rule), you need to boost the specificity of the second rule by at least 10.
One method would be to add a useless :not() to the second rule. This method is described in another answer and is allowed by the spec:
6.6.7. The negation
pseudo-class
Note: the :not() pseudo allows useless selectors to be written. For
instance :not(*|*), which represents no element at all, or
foo:not(bar), which is equivalent to foo but with a higher
specificity.
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid:not(.bar) {
padding:50px;
background-color: green;
}
<div class="grid">Test</div>
specificity calculator

CSS selector first-child does not work [duplicate]

This question already has answers here:
:first-child not working as expected
(5 answers)
Closed 7 years ago.
I have tried to use :nth-child(n), which does not work as well, am I using this selector on the wrong elements?
on the element div with the following class, block_photo.
.block_photo:first-child, .block_video:first-child {
margin-left:0px;
}
http://tmptstdays4god.ucoz.com/
Your html markup is the following:
<section class="new photos">
<h4 class="title">...</h4>
<div class="block-photo">...</div>
<div class="block-photo">...</div>
...
</section>
first-child / nth-child
Matches an element if it is the first child.
.block_photo:first-child {
/* styles here apply to the .block_photo IF it is the first child */
}
In your case, because the first child is <h4>, the selector .block_photo:first-child matches no element.
first-of-type / nth-of-type
Matches the first element of a certain type
.block_photo:first-of-type {
/* styles here apply for the first .block_photo element */
/* that is what you need */
}
References:
W3C specification on first-child
W3C specification on first-of-type

set only the css style of first adjacent div with a class name

How can I set the style of only the first div that has class "bla"? (not the second).
<div class="outer">
<div>
....(more div's, unknown how many)
<div class="bla">
....
<div class="bla">some content</div>
</div>
....
</div>
</div>
I'm assuming with this answer that by adjacent elements you mean sibling elements. If you were referring to parent-child elements then go with N1xx1's answer. That being said...
You can't target the first bla with css selectors alone. But you can target all the blas but the first. So, one possibility is to set the styles you want only on the first bla on all blas. Then override those styles by targeting all blas but the first. Like so:
.bla {
...styles for the first bla..
}
.bla ~ .bla {
...override styles set on first bla that you dont want on the others
}
The tilde between the two ".bla"'s is called the general sibling selector. If you've never heard of it, head on over to css selectors spec.
You can do simple workaround for this since you can't do that with any special selector:
.bla {
/* style here, example: */
background-color: #f00;
}
.bla .bla {
/* negate the style, example: */
background-color: transparent;
}
I hope this is what you were looking for.
According to pure css, you can't select according to the ordering of the html elements. Search the spec (here: http://www.w3.org/TR/CSS2/selector.html). There is nothing that refers to how many or in what order html elements match the given selectors.
Javascript:
getElementsByClass('bla')[0].style
EDIT: JOPLOmacedo provided a CSS only (better) answer
I've also found a way to select for instance the second <p> after a <h1> tag:
h1 + p + p{
background: red;
}
Just thought I'd share that.

how can I compound css selectors together

is it possible to use a :not() selector with a :nth-of-type(1) selector?
i.e.
I want to select the first that doesn't have the title "something"
<html>
<head>
<style type="text/css">
p
{
color:#000000;
}
p:not([title=something]):nth-of-type(1)
{
color:#ff0000;
}
</style>
</head>
<body>
<h1>This is a heading</h1>
<p title="something">This is a paragraph.</p>
<p>This is another paragraph.</p>
<p>This is another paragraph.</p>
<div>This is some text in a div element.</div>
</body>
</html>
The nth-of-type is acting on the original selector (p), it's not acting on the result of p:not([title=something]).
p:not([title=something]):nth-of-type(1)
This is saying, find the <p> without a title of "someting" that is also the 1st <p> on the page. This doesn't find any elements as the 1st <p> has the title "something".
What you want is the 1st <p> that doesn't contain the title "something". I don't know if CSS has a good way of doing that.
If you're willing to use jQuery, you can use do this:
$('p:not([title="something"]):eq(0)')
or:
$('p').not('[title="something"]').eq(0)
The problem is that the nth-of-type pseudo-class is defined as:
[nth-of-type] matches elements on the basis of their positions within a parent element’s list of child elements.
So the pseudo-class :nth-of-type(1) is limiting your selection to the p child at position 1.
Your pseudo-class not([title=something]) is limiting your selection to the p elements without the attribute/value title='something', just as you suspect.
The two selectors together are resulting in no elements because the p child at position 1 has title='something'.
For a better understanding, try the following:
p:nth-of-type(1) { color: red; }
p:not([title=something]) { text-decoration:underline; }
More information: Pseudo-classes, nth-of-type
As mentioned by the other answers, :nth-of-type() only refers to the element type, which in this case is p. The selector p:not([type=something]):nth-of-type(1) simply means a p element that is :not([type=something]) and is also the first p.
Anyway, what you're asking can be done in pure CSS using the general sibling selector, but may involve unnecessarily verbose and repetitive selectors:
p:not([title=something]) ~ p:not([title=something])
{
color:#000000;
}
p:not([title=something])
{
color:#ff0000;
}
If you just want to apply this to p elements without a title attribute, you can shorten your selectors a little:
p:not([title]) ~ p:not([title])
{
color:#000000;
}
p:not([title])
{
color:#ff0000;
}
I came up with this technique for use with classes first, which I describe in greater detail here, but it can be applied to a number of things, including attributes, for which I have another example here.

Resources