omega
omega

Reputation: 43843

How to avoid a specific ID inside element in CSS?

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

Answers (5)

LarsW
LarsW

Reputation: 1588

Best solution (although still not perfext):

(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.

Incorrect, failing solution (just to show what's wrong):

#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

C3roe
C3roe

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

Amit
Amit

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

Rion Williams
Rion Williams

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 :

enter image description here

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 :

enter image description here

Upvotes: -1

stckvrw
stckvrw

Reputation: 1791

Why not do simply:

#A a {
 color:#fff;
}
#B a {
 color:green;
}

Upvotes: 2

Related Questions