CSS selector for something not inside something else - html

I have some nested elements like this:
<div class="foo">
<div class="select-inside-this">
<div class="not-inside-this">
<div class="one select-this"></div>
</div>
</div>
</div>
<div class="select-inside-this">
<div class="two select-this"></div>
</div>
<div class="three select-this"></div>
I want to select all .select-this which are inside .select-inside-this but not those which are wrapped in .not-inside-this. So in the end, i should be able to select only two.select-this from the above code.
The CSS I've tried but did not work:
.select-inside-this :not(.not-inside-this) .select-this {
/* style here /*
}
or:
.select-inside-this *:not(.not-inside-this) .select-this {
/* style here /*
}
Any workaround here?
I don't want to use JavaScript here. I need pure CSS3 solution.
EDIT: I don't want to use direct child (>) selector. As I've asked, I want to select all those element from any level just without the exception wrapper.

:not(.not-inside-this) and *:not(.not-inside-this) with the * are equivalent; in the case of the former, the universal selector is implied. See the spec.
It is currently not possible to construct a CSS selector that matches elements that are not descendants of specific elements for the reasons given in the following questions:
CSS negation pseudo-class :not() for parent/ancestor elements
Is the CSS :not() selector supposed to work with distant descendants?
The selector
.select-inside-this :not(.not-inside-this) .select-this
matches .select-this elements that are descendants of some element that is not .not-inside-this, which in turn is a descendant of .select-inside-this. It does not match .select-this elements that are not descendants of .not-inside-this within .select-inside-this.
This means, first off, that your selector will incorrectly match the following:
<div class="select-inside-this">
<div class="bar">
<div class="not-inside-this">
<div class="select-this"></div>
</div>
</div>
</div>
... because one of the ancestors of .select-this, .bar, is :not(.not-inside-this).
Additionally, this implies at least three levels of nesting (though it could be more). In your example, there are no other elements between .two.select-this and its containing .select-inside-this, so it will never match that element. This is why James Donnelly suggests adding .select-inside-this > .select-this to account for that particular case.
However it is still not possible to write a single complex selector using descendant combinators to match elements without a specific ancestor. The only way is to repeat the child combinator method with as many :not(.not-inside-this) as necessary, but this requires that you account for all possible cases. If you can't do that, then you're out of luck with CSS selectors.

You can use the Child Combinator Selector > to specify direct children:
.select-inside-this :not(.not-inside-this) .select-this,
.select-inside-this > .select-this {
/* style here /*
}
This selects any .select-this element which is not a descendent of any .not-inside-this element and also selects .select-this elements which are direct children of .select-inside-this elements.
body > .select-inside-this :not(.not-inside-this) .select-this,
body > .select-inside-this > .select-this {
color: red;
}
<div class="foo">
<div class="select-inside-this">
<div class="not-inside-this">
<div class="one select-this">
This should not be selected
</div>
</div>
</div>
</div>
<div class="select-inside-this">
<div class="two select-this">
This should be selected
</div>
</div>
<div class="three select-this">
This should not be selected
</div>

A little bit late to the party, and it might not match your use case, but this is what I ended up doing:
HTML:
<div class="foo">
<div class="select-inside-this">
<div class="not-inside-this">
<div class="one select-this"></div>
</div>
</div>
</div>
<div class="select-inside-this">
<div class="two select-this"></div>
</div>
<div class="three select-this"></div>
CSS:
.select-inside-this .select-this {
background: blue;
}
.select-inside-this .not-inside-this .select-this {
background: none;
}
The trick is to positively select the negative element and just undo the style.
It'll work for simple use cases, at the very least.

I ended up
styling but hiding the styles by default, and then
revealing them on the nested element only.
Example with image backgrounds:
.box{
height:100px;
background-image: url("img.jpg");
background-repeat: no-repeat;
background-position: top 100px left 0; /*hide by default (here by shifting position)*/
}
.container .box{
background-position: top left; /*reveal in the nested*/
}
Hope you find a way to hide the style you need in place in a similar way.

Related

Change another div on hover which is not sibling nor child

My HTML code is similar to this :
<div class="navbar">
<div class="btn-section btn-1">
<p class="section pro">...</p>
</div>
</div>
<div class="navbar">
<div class="btn-section btn-2">
<p class="section notpro">...</p>
</div>
</div>
I'm using this in my CSS code :
.btn-1:hover {
.pro {
...
}
}
It works perfectly.
What I want to do now is to modify my .notpro class inside the btn-1:hover. As .notpro is not child or sibling with btn-1, it doesn't work.
.btn-1:hover {
.pro {
... // works
}
.notpro {
... // doesn't work
}
}
Is there a way to do this ?
Thank you !
There is no way without using javascript to affect a different non-sibling selector. But you an do it if you move the hover up one level.
You need to put the hover on the first navbar and using the direct sibling combinator (+) - target the other navbar and then inside it to get the .notpro element. Note that I added words to your elements to show the hover effect.
The only other CSS way of doing this is to put both elements inside the one navbar - then they are siblings and can be targetted.
.navbar:hover + .navbar .notpro {
color: red;
}
<div class="navbar">
<div class="btn-section btn-1">
<p class="section pro">I am a Pro</p>
</div>
</div>
<div class="navbar">
<div class="btn-section btn-2">
<p class="section notpro">I am Not a Pro</p>
</div>
</div>
I don't think this syntax is valid in CSS, meaning selector inside another selector.
You can add :hover to the 'pro' to get different behaviour than the parent div.

CSS selector every element except those inside a particle class

I'm trying to select every element within a wrapper except the elements within one of the children. Consider this:
<div class="wrapper">
<div class="this">
<div class="that"></div>
</div>
<div class="foo">
<div class="bar"></div>
<div class="orange">
<div class="ignore"></div>
</div>
</div>
<div class="hello"></div>
<div class="world">
<div class="ignore">
<div class="this"></div>
</div>
</div>
</div>
What I want to do is to make the text color of everything inside wrapper white, except the elements that are inside ignore. What I got so far is .wrapper *:not(.ignore *), which doesn't work.
EDIT: I can't accept solutions that include overriding what the color is within .ignore because that color is pre-set, and is out of my control. It is also impossible to know which color is used in the pre-set. Imagine there's a body {color:blue;}, only in my case, it's impossible to know what color it is.
Add color: #fff to .wrapper
Then, add whatever color your want to .ignore
After that, make sure .ignore loads after .wrapper in your style sheet.
.wrapper {
background: #131418;
color: #fff;
font-size: 25px
}
.ignore {
color: #933
}
<div class="wrapper">
<div class="this">
<div class="that">wrapper</div>
</div>
<div class="foo">
<div class="bar">wrapper</div>
<div class="orange">
<div class="ignore">ignore</div>
</div>
</div>
<div class="hello">wrapper</div>
<div class="world">
<div class="ignore">
<div class="this">ignore</div>
</div>
</div>
</div>
If you put them in right order you can get this:
.wrapper {
background: green;
}
.wrapper *:not(.ignore) {
color: white;
}
.wrapper *, .wrapper .ignore *{
color: red;
}
<div class="wrapper">
<div class="this">
<div class="that">1</div>
</div>
<div class="foo">
<div class="bar">2</div>
<div class="orange">
<div class="ignore">3</div>
</div>
</div>
<div class="hello"></div>
<div class="world">
<div class="ignore">
<div class="this">4</div>
</div>
</div>
</div>
Note that :not(...) is applied to the current element, so you can't use :not(something [some element inside])
I'd suggest:
.wrapper div:not(.ignore) {
color: white;
}
The reason your posted CSS selector doesn't work – and shouldn't be expected to work – is because:
.wrapper *:not(.ignore *)
Is trying to select all descendent elements that are not descendants of the .ignore elements, whereas in your question it seems that you're trying to select only elements that are not themselves of the .ignore class.
Further, the :not() pseudo-class:
...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.
[Emphasis mmine, https://www.w3.org/TR/css3-selectors/#negation].
And a 'simple selector' is:
...either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
[https://www.w3.org/TR/css3-selectors/#simple-selectors-dfn]
Which appears to prevent the use of a combinator, the white-space, representing the selection of a descendant; meaning that your selector .ignore * is an invalid selector for the negation (:not()) pseudo-class.
Pure CSS doesn't seem to provide a good solution - at least not one I can think of.
The problem with not is it can only apply to "simple selectors", which basically means the selector it applies to can't contain combinators like whitespace.
For simple cases, you could do what a lot of people are suggesting - just have a second rule that selects .ignore * and undoes what your .wrapper * rule does. But if the .wrapper * rule does a lot, or if the exact state you'd get without the .wrapper * rule is unclear (maybe set by an external resource) then that isn't necessarily practical.
What you could do is use JavaScript (or similar) to propagate the .ignore class down to all of its descendants, then just use :not(.ignore)

nth-child(even) for a css class only not all nodes

Trying to only color certain every other div of class 'story':
<div class="wrap-well">
<div class="story">odd</div>
<div class="story">even</div>
<div class="clearfix"></div>
<div class="story">odd</div>
<div class="story">even</div>
<div class="clearfix"></div>
<div class="story">odd</div>
<div class="story">even</div>
</div>
CSS:
.wrap-well div.story:nth-child(even) {
background-color:#ff00ff;
}
Fiddle:
http://jsfiddle.net/NF2dk/
But it seems that 'clearfix' columns are also counted...
#Marcin and #Explosion Pills is absolutely right here, but as I inspected your DOM, you've a consistent pattern going on there, you can use Adjacent selector to achieve this rather than using nth-child or nth-of-type
.wrap-well div.story + div.story {
background-color:#ff00ff;
}
Demo
This way, it will just do the job what you wanted to achieve, also it's much more compatible compared to nth pseudos
nth-child does not work with the selector, but the element. It selects each even div regardless of the composition of the selector.
You can use nth-of-type to only select <div> elements and use another element such as <br> for the clearfix.
http://jsfiddle.net/NF2dk/1/
There is nothing like nth-of-class() selector.
The closest you can get is nth-of-type(). But it will look at the element tag, not class assigned to the element.

:last-child not working as expected?

The issue lies within this CSS and HTML. Here is a link to jsFiddle with the sample code.
HTML
<ul>
<li class"complete">1</li>
<li class"complete">2</li>
<li>3</li>
<li>4</li>
</ul>
CSS
li.complete:last-child {
background-color:yellow;
}
li.complete:last-of-type {
background-color:yellow;
}
Shouldn't either of these lines of CSS target the last li element with the "complete" class?
This query in jQuery doesn't target it either:
$("li.complete:last-child");
But this one does:
$("li.complete").last();
li {
background-color: green;
}
li.complete:first-child {
background-color: white;
}
li.complete:first-of-type {
background-color: red;
}
li.complete:last-of-type {
background-color: blue;
}
li.complete:last-child {
background-color: yellow;
}
<ul>
<li class="complete">1</li>
<li class="complete">2</li>
<li>3</li>
<li>4</li>
</ul>
:last-child will not work if the element is not the VERY LAST element
I think it's crucial to add/emphasize that :last-child will not work if the element is not the VERY LAST element in a container. For whatever reason it took me hours to realize that, and even though Harry's answer is very thorough I couldn't extract that information from "The last-child selector is used to select the last child element of a parent."
Suppose this is my selector: a:last-child {}
This works:
<div>
<a></a>
<a>This will be selected</a>
</div>
This doesn't:
<div>
<a></a>
<a>This will no longer be selected</a>
<div>This is now the last child :'( </div>
</div>
It doesn't because the a element is not the last element inside its parent.
It may be obvious, but it was not for me...
Sidebar: This may seem like a ridiculous gotcha, but the devil's always in the details. :last-of-type may fit your needs in most cases (and feels intuitive) but :last-child definitely serves a purpose. It offers greater specificity (targeting only those elements which are, in-fact, the very last child in a parent). It depends on your use-case.
The last-child selector is used to select the last child element of a parent. It cannot be used to select the last child element with a specific class under a given parent element.
The other part of the compound selector (which is attached before the :last-child) specifies extra conditions which the last child element must satisfy in-order for it to be selected. In the below snippet, you would see how the selected elements differ depending on the rest of the compound selector.
.parent :last-child{ /* this will select all elements which are last child of .parent */
font-weight: bold;
}
.parent div:last-child{ /* this will select the last child of .parent only if it is a div*/
background: crimson;
}
.parent div.child-2:last-child{ /* this will select the last child of .parent only if it is a div and has the class child-2*/
color: beige;
}
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div>Child w/o class</div>
</div>
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child-2'>Child w/o class</div>
</div>
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<p>Child w/o class</p>
</div>
To answer your question, the below would style the last child li element with background color as red.
li:last-child{
background-color: red;
}
But the following selector would not work for your markup because the last-child does not have the class='complete' even though it is an li.
li.complete:last-child{
background-color: green;
}
It would have worked if (and only if) the last li in your markup also had class='complete'.
To address your query in the comments:
#Harry I find it rather odd that: .complete:last-of-type does not work, yet .complete:first-of-type does work, regardless of it's position it's parents element. Thanks for your help.
The selector .complete:first-of-type works in the fiddle because it (that is, the element with class='complete') is still the first element of type li within the parent. Try to add <li>0</li> as the first element under the ul and you will find that first-of-type also flops. This is because the first-of-type and last-of-type selectors select the first/last element of each type under the parent.
Refer to the answer posted by BoltClock, in this thread for more details about how the selector works. That is as comprehensive as it gets :)
I encounter similar situation. I would like to have background of the last .item to be yellow in the elements that look like...
<div class="container">
<div class="item">item 1</div>
<div class="item">item 2</div>
<div class="item">item 3</div>
...
<div class="item">item x</div>
<div class="other">I'm here for some reasons</div>
</div>
I use nth-last-child(2) to achieve it.
.item:nth-last-child(2) {
background-color: yellow;
}
It strange to me because nth-last-child of item suppose to be the second of the last item but it works and I got the result as I expect.
I found this helpful trick from CSS Trick

Is there a CSS selector for the first direct child only?

I have the following html
<div class="section">
<div>header</div>
<div>
contents
<div>sub contents 1</div>
<div>sub contents 2</div>
</div>
</div>
And the following style:
DIV.section DIV:first-child
{
...
}
For some reason that I don't understand the style is getting applied to the "sub contents 1" <div> as well as the "header" <div>.
I thought that the selector on the style would only apply to the first direct child of a div with a class called "section". How can I change the selector to get what I want?
What you posted literally means "Find any divs that are inside of section divs and are the first child of their parent." The sub contains one tag that matches that description.
It is unclear to me whether you want both children of the main div or not. If so, use this:
div.section > div
If you only want the header, use this:
div.section > div:first-child
Using the > changes the description to: "Find any divs that are the direct descendents of section divs" which is what you want.
Please note that all major browsers support this method, except IE6. If IE6 support is mission-critical, you will have to add classes to the child divs and use that, instead. Otherwise, it's not worth caring about.
Found this question searching on Google. This will return the first child of a element with class container, regardless as to what type the child is.
.container > *:first-child
{
}
CSS is called Cascading Style Sheets because the rules are inherited. Using the following selector, will select just the direct child of the parent, but its rules will be inherited by that div's children divs:
div.section > div { color: red }
Now, both that div and its children will be red. You need to cancel out whatever you set on the parent if you don't want it to inherit:
div.section > div { color: red }
div.section > div div { color: black }
Now only that single div that is a direct child of div.section will be red, but its children divs will still be black.
The CSS selector for the direct first-child in your case is:
.section > :first-child
The direct selector is > and the first child selector is :first-child
No need for an asterisk before the : as others suggest. You could speed up the DOM searching by modifying this solution by prepending the tag:
div.section > :first-child
Use div.section > div.
Better yet, use an <h1> tag for the heading and div.section h1 in your CSS, so as to support older browsers (that don't know about the >) and keep your markup semantic.
div.section > div
Not exactly the question asked, but maybe useful:
div.section > :first-child:is(div)
This would match only the first child element of .section and only if it was a div.
Match:
<div class="section">
<div>MATCH</div>
<div>NO MATCH</div>
<div>
<div>NO MATCH</div>
</div>
</div>
No match:
<div class="section">
<img ... >
<div>NO MATCH</div>
<div>NO MATCH</div>
<div>
<div>NO MATCH</div>
</div>
</div>
This is how I solved when using TailwindCSS (v3.1) with arbitrary variants.
I only wanted the first column in table to be underlined when hovered, as it is a link.
[&>:first-child]:hover:underline