Reputation: 43843
I have this structure in html
<div id="A">
....
<div id="B">
....
</div>
....
</div>
How can I write a CSS rule, that says, make all a
tags color white inside #A, but ignore what's in #B?
I would prefer to have something like :not(#B)
and not put another wrapper tag or anything too hardcoded.
Thanks
Upvotes: 2
Views: 791
Reputation: 1588
(Corrected after the comment and with the code of @Amit)
/* Either directly under #A, or in an element in #A that's not #B */
/* The element that's not #B must be a direct child of #A, otherwise */
/* children of children of #B will be selected anyway, as @Amit pointed out. */
#A > a, #A > :not(#B) a { color:red }
<div id="A">
<a>red</a>
<div id="B">
<a>black</a>
<p>
<a>black</a>
</p>
</div>
<p>
<a>red</a>
</p>
</div>
This still has problems (IE 9+ and not working if #B
is wrapped), but it is the best solution we've got.
#A > a, #A :not(#B) a { color:red }
<div id="A">
<a>red</a>
<div id="B">
<a>black</a>
<p>
<a>black</a>
</p>
</div>
<p>
<a>red</a>
</p>
</div>
Upvotes: 7
Reputation: 96306
You’re on the right track with :not(#B)
already.
You want to format the links that are direct children of #A, and those that are further down the tree, but not those in #B.
/* edited, was previously just #A > a, #A :not(#B) a, which won’t work for deeper nesting
inside #B, as Amit pointed out */
#A > a, #A > :not(#B) a { color:green; }
/* for illustration purposes only */
#B { border:1px solid red; }
#B:before { content:"[I’m #B, my links aren’t green.]"; display:block; }
p { border:1px solid yellow; }
p:before { content:"[I’m a paragraph, the link inside me is not a child of #A.]"; display:block; }
<div id="A">
<a href="#">Link</a>
<div id="B">
<a href="#">Link</a>
<span><a href="#">Link inside span</a></span>
</div>
<p>
<a href="#">Link</a>
</p>
</div>
Edit: As Amit pointed out, #A :not(#B) a
would not work for links nested deeper into #B. So the :not(#B)
part has to be a child of #A, #A > :not(#B) a
. Example edited.
Upvotes: 2
Reputation: 46323
There's no solution that "just works" without restrictions. Your best effort would be to set explicit rules to elements within your negated selector (:not(#B)
).
The reason for this is that rules are evaluated "positively", they look for a positive match, so for example (taken from one of the other "inaccurate" answers):
#A > a, #A :not(#B) a { color:green; }
/* for illustration purposes only */
#B { border:1px solid red; }
#B:before { content:"[I’m #B, my links aren’t green.]"; display:block; }
p { border:1px solid yellow; }
p:before { content:"[I’m a paragraph, the link inside me is not a child of #A.]"; display:block; }
<div id="A">
<a href="#">Link</a>
<div id="B">
<span>
<a href="#">I am green after all</a>
</span>
</div>
<p>
<a href="#">Link</a>
</p>
</div>
The <span>
around the link serves as a positive match for :not(#B)
, and the logic breaks.
Perhaps the closest you can get is by restricting matches the direct children plus nested children whose top most parent under A
is not B
:
#A > a, #A > :not(#B) a { color:green; }
<div id="A">
<a href="#">Link</a>
<div id="B">
<span>
<a href="#">I am really not green</a>
</span>
</div>
<p>
<a href="#">Link</a>
</p>
</div>
But this would also break as soon as any element wraps B
.
Upvotes: 2
Reputation: 76557
If you are actually trying to target <a>
tags that appear under these elements and had markup that looked like the following :
<div id="A">
<a href='#'>Test A1</a>
<div id="B">
<a href='#'>Test B</a>
</div>
<a href='#'>Test A2</a>
</div>
You could take advantage of the direct descendant operator >
in CSS to only target elements directly below #A
and not within it's children :
#A > a {
/* This will only target <a> elements that are beneath #A and not in #B */
color: #FFF;
}
And example of this can be seen here and might look like :
Update
It looks like you don't want to just target <a>
tags. If that is the case, you could probably generalize the previous statement by only targeting elements not in B under A :
#A > :not(#B) {
color: #FFF;
}
Updating the example markup :
<div id="A">
<a href='#'>Test A1</a>
<div id="B">
<a href='#'>Test B</a>
</div>
<div id="C">
I'm in C
</div>
<a href='#'>Test A2</a>
still will work as expected :
Upvotes: -1