css :not selector to target everything but a condition - html

http://jsfiddle.net/m7qLdstp/1/
<style>
:not(div p), p{
color: red
}
</style>
<p>This is a paragraph that should be red.</p>
<div><p>This is a paragraph that should not be red.</p></div>
Is it possible to use the css :not selector to (in this case) turn all <p> color red, except for any <p> instead a <div>?
I ran a few different variations on jsfiddle but cannot get it to work?!

The following will change the color of all <p> elements that are not direct descendants of a <div>:
:not(div) > p{
color: red;
}
Demo fiddle

You could just target them separately
p{
color: red
}
div p{color:black}
fiddle
it works because div p is more specific than p

According to CSS-Tricks, :not() selector can take only a "simple selector", defined as:
a Type Selector, Universal Selector, Attribute Selector, Class Selector, ID Selector, or Pseudo Class Selector
but:
may not contain additonal selectors or any pseudo-element selectors.
However, as other answers suggest, there are ways you could work around this issue and accomplish the behavior at least in this example case and other simple cases.

Related

CSS not() with attribute selector doesn't seem to work

I have a very simple selector that works, but when adding it to a :not() it no longer seems to recognize it.
h2:not([random-attribute~="value"] h2){
color: red;
}
[random-attribute~="value"] h2{
color: blue;
}
<div class="content">
<h2>Same valid selector, not working</h2>
<div random-attribute="value">
<h2>Valid selector turned blue.</h2>
</div>
</div>
From what I understand, if you put a valid selector inside the not() you will get any h2 element that is not whatever is inside the parenthesis. This is intuitive.
What isn't intuitive, is that the selector within the not() is valid and works when used alone, but when added to the not() it doesn't seem to work.
Is this not a valid way to write this?
You need to style all h2 element that are descendants of elements that are not [random-attribute~="value"] then style h2 that are.
It doesn't hurt to qualify the selector with a direct child combinator too.
Like so:
*:not([random-attribute~="value"]) > h2 {
color: red;
}
[random-attribute~="value"] > h2 {
color: blue;
}
<div class="content">
<h2>Same valid selector, not working</h2>
<div random-attribute="value">
<h2>Valid selector turned blue.</h2>
</div>
</div>
<h2>some other heading</h2>
You have the syntax wrong for ([random-attribute~="value"] h2) It should just be ([random-attribute~="value"]). See below:
h2:not([random-attribute~="value"]){
color: red;
}
[random-attribute~="value"] h2{
color: blue;
}
<div class="content">
<h2>Same valid selector, not working</h2>
<div random-attribute="value">
<h2>Valid selector turned blue.</h2>
</div>
</div>
You are only supposed to put the given attribute in :not(), not the actual element.
In Selectors Level 3, :not only supports a simple selector argument. That will probably change in Selectors Level 4, but browsers don't support it yet.
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.
Note: In Selectors Level 3, only a single simple selector was allowed
as the argument to :not().
Meanwhile, you can rewrite
h2:not([random-attribute~="value"] h2)
as
:root:not([random-attribute~="value"]) > h2,
:root:not([random-attribute~="value"]) > :not([random-attribute~="value"]) > h2,
:root:not([random-attribute~="value"]) > :not([random-attribute~="value"]) > :not([random-attribute~="value"]) > h2
/* ... repeat until you get deep enough */
However, instead of using complicated selectors like that, in CSS it's more natural to let the cascade pick the most specific styles. As kristóf baján recommends, you don't even need :not:
h2 {
/* Default styles */
}
[random-attribute~="value"] h2 {
/* Overriding styles */
}
I think you are making your job a little too complicated... :)
You should just use:
[random-attribute="value"] h2{
...
}
h2 {
...
}
This should solve your problem. The reason behind the fact that it is not working as YOU would expect it to is that the selector inside the not operator is supposed to extend the clarification of the element and not its parent.

Why ::selection:first-child doesn't work?

Why this code doesn't work?
::selection:first-child{
background-color: #ffa563;
}
This works fine - :first-child:last-child (when element is first child AND last child)
::selection is not an element. try :first-child::selection should work.
:first-child::selection {
color: red;
}
<div>First Child</div>
<div>Second Child</div>
You mixing up pseudo class and pseudo element. ::selection is pseudo element, so it doesn't contain anything and only work with few css attributes.
If you want to style selection in first child, use :first-child::selection instead
When you try to use a selector like "::selection" or ":first-child" it´s better if you specify the container.
<div>
<p>text 1</p>
<p>text 2</p>
</div>
and the css:
div p:first-child::selection {
color: red;
background: yellow;
}
the first 'p' will show different if you select.
Note that :first-child will never adjust to the selectors listed before. For example, p:first-child will not match for a structure of <div><h1></h1><p></p></div> since the p is the second tag.
Similarly, a ::selection:first-child would also not match the first tag in the selection but only a selected element that is also a first child. And at that point, it’s equivalent to :first-child::selection.
As for why it doesn’t work, ::selection is a pseudo element. Using it creates some kind of an element that is matched. But that pseudo element never exists in the DOM, so it cannot be the first child of something. So the whole selector will not match.

Why doesn't css :not work in some cases?

Why does the :not selector not work at all in this case:
a {
color:red;
}
div a:not(.special a) {
color: green;
}
In, for example:
<div>
<span>hellolink</span>
</div>
<div class="special">
<span>hellolink</span>
</div>
<p>
something else
</p>
Demo: https://jsfiddle.net/eguecrvz/
:not() currently doesn't allow combinators or any more than one simple selector (such as .special or a) as an argument. A future specification will expand :not() to accept any number of complex selectors, and once browsers support it your selector div a:not(.special a) will work exactly as intended.
In the meantime, it is not possible to write a selector for "an a element that does not have a .special ancestor element." You will need to override with an additional .special a selector in your rule that applies to a elements in general:
a, .special a {
color:red;
}
div a {
color: green;
}
The additional specificity of .special a will ensure that it overrides div a, but if specificity is a problem you will need to rearrange your rulesets.
special is class of div not anchor.
Change css like:
div:not(.special) a{
color: green;
}
Fiddle
Edit:
If you want all link green except .special the do like:
a {
color:green;
}
.special a{
color: red;
}
Updated Fiddle
Why doesn't css :not work in some cases?
div a:not(.special a) {
color: green
}
Because the negation pseudo-class (:not) only takes a simple selector as an argument.
Your argument (.special a) represents a descendant selector which does not qualify.
6.6.7. The negation
pseudo-class
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.
What is a simple selector?
4. Selector syntax
A simple selector is either a type selector, universal selector,
attribute selector, class selector, ID selector, or pseudo-class.

CSS Sibling selector doesn't work inside :not()

I know that with pure CSS it's impossible to select previous siblings of the element. But I try to fool browser with a complex selector.
Please see this jsFiddle. It contains several CSS rules that work fine except 2 of them:
//set border color to white for all elements before .selected and .selected itself
div:not(.selected ~ div) {
border-color: #fff;
}
//set border color to green for the element previous to .selected
div:not(.selected ~ div):nth-last-child(2) {
border-color: #0f0;
}
But seems that inside :not() sibling selector ~ doesn't work.
So there're 2 questions:
Is it expected that ~ doesn't work inside :not()?
Is there any work-around for such case?
EDIT:
The final idea is to make a nice hover effect with pure CSS like:
The hovered image is simply scaled, image to right of it could be found and styled easily but the left one... The example with divs is just an example.
:not() pseudo-class could not contain sibling selector.
http://dev.w3.org/csswg/selectors3/#negation
The negation pseudo-class, :not(X), is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument.
http://dev.w3.org/csswg/selectors3/#simple-selectors-dfn
A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
If you want to :
set border color to white for all elements before .selected and
.selected itself
I would suggest to default the border color to white and change it after .selected :
div {
display: inline-block;
border: 1px solid #fff;
width: 30px;
}
div.selected ~ div {
border-color: #000;
}
<div>A</div>
<div>B</div>
<div class="selected">C</div>
<div>D</div>
<div>E</div>
<div>F</div>
<div>G</div>
For the second rule :
set border color to green for the element previous to .selected
If you know wich one will be selected in advance, you can target the element before with nth-child() otherwise you will need some JS to select it.
An other approach if the .selected class is dynamicaly added would be to use the same mechanism (PHP, JS or other) to give a class to the privious element at the same time and apply CSS to that class.
It is possible since CSS Level 4.
https://www.w3.org/TR/selectors-4/
The negation pseudo-class, :not(), is a functional pseudo-class taking a selector list as an argument.
Also it is changed from :not( <selector># ) to :not( <complex-selector-list> ) in https://developer.mozilla.org/en-US/docs/Web/CSS/:not#syntax in November 2018.
See browser compatibility for "Selector list argument" in https://developer.mozilla.org/en-US/docs/Web/CSS/:not#browser_compatibility.

How to apply style for first heading only if it more than one?

I may have two types of html...
One:
<div>
<h4></h4><!--not to this-->
<p></p>
</div>
Two:
<div>
<h4></h4><!--this should be styled--->
<h4></h4>
<p></p>
</div>
All styling are the same but just border-bottom to h4 of first h4 tag only if it contains two h4 tags as in the example. How to do without changing html?
You can combine :first-child, :not() and :only-of-type pseudo-classes to achieve that.
Here you go:
h4:first-child:not(:only-of-type) {
background-color: gold;
}
WORKING DEMO.
This selector represents the <h4> element which is the first child of its parent whereas it's not the only of TYPE of elements in the children tree of the parent.
From the MDN:
The :only-of-type CSS pseudo-class represents any element that has
no siblings of the given type.
Let's go Crazy!
If the <h4> element is not the first child of its parent, we can select the first <h4> element and achieve the same effect by using :first-of-type pseudo-class as follows:
h4:first-of-type:not(:only-of-type) {
background-color: gold;
}
UPDATED DEMO.
For further details on :first-of-type vs :first-child you can refer my answer here.
you need to style the border-bottom of your 1st h4 only if the parent contains two adjacent headings
you could then style the border-top of the 2nd h4 and obtain the same effect
h4 + h4 {
border-top: ...
}
When you have one heading only, no style will be applied. If you have two or more adjacent headings, a border between them will be applied
This is what you need:
h4:first-child:nth-last-of-type(n+2)
{
color:green;
}
FIDDLE
You can use the First-child class.
I could look like this:
div h4:first-child{
CODE HERE
}
I think you are better off styling the second h4 if possible, as you would not be able to tell with CSS whether there are one or two h4's in the div.
You can do this with nth-child
div h4:nth-child(2) {
// your styles.
}
Fiddle