0

I have a .header div with a span maindomain and a div otherdomains inside of it:

<div class="header"><span class="maindomain">LatestFooty.co.uk</span> is currently available for sale, along with:
<div class="otherdomains">    
LatestFootie.com<br>
LatestFootie.co.uk
</div>
</div>

I'm trying to target the is currently available for sale, along with:, without touching the contents of .maindomain or .otherdomains. I understand that the best approach to this might be to wrap it in a span and target that instead, but at this point I'd like to figure out why I can't get the :not pseudo-class working.

Here is what I have:

@media (min-width:300px) and (max-width:450px) {
  .header:not(.maindomain):not(.otherdomains) {
  font-style: italic;  
  }
}

As far as I can tell, the syntax is correct, and I don't think it's a specificity issue because !important doesn't make a difference. What am I doing wrong?

Hashim Aziz
  • 4,074
  • 5
  • 38
  • 68

4 Answers4

1

.header:not(.maindomain):not(.otherdomains) only targets elements which have the .header class and don't have the .maindomain and/or the .otherdomain class themselves.

Your rules currently say:

<div class="header"> is targeted

<div class="header maindomain"> is not targeted

<div class="header otherdomains"> is not targeted

<div class="header maindomain otherdomains"> is not targeted

But this is not what you want to do here obviously.

You cannot apply rules to the .header class depending on classes of its children with CSS alone.

There's an approved answer to your question here which might guide you in the right direction (using JavaScript or jQuery in that case).

Tad Wohlrapp
  • 1,848
  • 1
  • 13
  • 18
  • So I've misunderstood how `:not()` works, fair enough. JavaScript or jQuery would be overkill so I think at this point it'd be better to just wrap a span around the text in question. Thank you. – Hashim Aziz May 01 '20 at 16:07
  • @Hashim that would be the best approach, yes. – Tad Wohlrapp May 01 '20 at 16:13
1

You will need two selectors:

.header {
  font-style:italic;
}
.header .otherdomains,
.header .maindomain {
  font-style:initial;
}

/* OR
.header * {
  font-style:initial;
}

*/
<div class="header"><span class="maindomain">LatestFooty.co.uk</span> is currently available for sale, along with:
<div class="otherdomains">    
LatestFootie.com<br>
LatestFootie.co.uk
</div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • I chose the other answer because it is the more direct answer to the question, but I've upvoted for explaining how this can be accomplished in CSS without `:not()`. – Hashim Aziz May 01 '20 at 17:51
0

I'm trying to target the "is currently available for sale, along with:", without touching the contents of .maindomain or .otherdomains.

You can't target anonymous elements in CSS.

CSS rules need a "hook" in the HTML to attach to. That hook is an HTML tag. Without the tag, CSS has nothing to target. This concept applies across box models.

From MDN:

An anonymous box is created when there is not an HTML element to use for the box. This situation happens when, for example, you declare display: flex on a parent element, and directly inside there is a run of text not contained in another element. In order to fix the box tree, an anonymous box is created around that run of text. It will then behave as a flex item, however, it cannot be targeted and styled like a regular box because there is no element to target.

(emphasis mine)

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

Everything is in the demo itself, the JavaScript is for demo purposes.

Demo

const lnx = [...document.links];
lnx.forEach(lnk => lnk.addEventListener('click', viewHTML));
function viewHTML(e) {
  const link = e.target;
  const headers = document.querySelectorAll('.'+this.dataset.tag);
  headers.forEach(hdr => {
    if (!hdr.matches('.hide')) {
      link.className = 'off';
      let str = hdr.outerHTML;
      let txt = document.createElement('div');
      txt.className = 'txt';
      hdr.insertAdjacentElement('afterend', txt);
      hdr.nextElementSibling.insertAdjacentText('beforeend', str);
      hdr.classList.add('hide'); 
    } else {
      link.className = '';
      hdr.classList.remove('hide');
      hdr.nextElementSibling.remove();
    }
  });
}
body {
  font: 400 2.5vw/1.5 Consolas
}

[class^=header] {
  font-family: Arial;
}

/* Header (OP)
Selector fails -- :not() is prefixed incorrectly
.header:...  means .header is targeted
.header :... means the descendants of .header is targeted
There is no .header.A, .header.B, nor .header.A.B 
so .header without .A and/or .B will have everything in italics
*/
.header:not(.A):not(.B) {
  font-style: italic;  
}

/* Header 1
Best solution with no extra HTML tags:
Assign font-style: normal...
directly (.C1, .D1)
or by class (.N)
*/
.header1 {
  font-style: italic;
}

.C1,
.D1,
.N {
  font-style: normal;
}

/* Header 2
Using :not() needs extra HTML tag:
Wrap second textnode in an inline or inline-block tag
As content of a descendant tag, the text can be targeted
*/
.header2 *:not(.E):not(.F) {
  font-style: italic;
}

/* Header 3
Smart solution with extra HTML tag:
Wrap second textnode in <i> or <em>
*/
.header3 {
  /* no styles needed */
}

/* Header 4
Slickest solution with least HTML:
Wrap text that needs italics in <i> and then style lines with CSS
*/
.header4 {
  white-space: pre-line;
}

/* For Demo Purposes */
.dash {
  border-style: dashed;
}

.edge {
  border-style: ridge;
  border-width: 3px;
}

summary:hover {
  color: lime;
  background: #000;
  cursor: pointer;
}

summary + u {
  display: inline-block;
  text-decoration: none;
  white-space: pre-line;
}

code {
  color: green;
  background: rgba(0, 0, 0, 0.2);
  white-space: pre;
}

summary + code {
  display: block;
}

a {
  display: block;
  text-decoration: none;
  text-align: center;
}

a:link, 
a:visited {
  color: cyan;
  background: #000;
}

a:hover,
a:active {
  color: blue;
  background: none;
}  

a::before {
  content: 'View .'attr(data-tag);
}

a.off::before {
  content: 'Hide .'attr(data-tag);
} 

a::after {
  content: ' HTML';
}

.hide {
  display: none;
}

.txt {
  color: blue;
  background: rgba(0, 0, 0, 0.2);
  white-space: pre;
}
<main>
<hr class='edge'>
<details><summary>Header (OP)</summary>
<u>Selector fails -- :not() is prefixed incorrectly
.header:...  means .header is targeted &#128078;
.header<code>&blank;</code>:... means the descendants of .header is targeted &#128077;
There is no .header.A, .header.B, nor .header.A.B so
.header <em>without</em> .A and/or .B will have everything in italics</u></details>
<details><summary>CSS</summary>
<code>.header:not(.A):not(.B) {
  font-style: italic;  
}</code>
<a href='#/' data-tag='header'></a>
</details>
<hr>

<div class='header'>
  <span class="A">LatestFooty.co.uk</span> is currently available for sale, along with:
  <div class="B">
    LatestFootie.com<br> LatestFootie.co.uk
  </div>
</div>

<hr class='edge'>
<details><summary>Header 1</summary>
<u>Best solution with no extra HTML tags:
Assign <code>font-style: normal</code>...
directly (.C1, .D1)
or by class (.N)</u></details>
<details><summary>CSS</summary>
<code>.header1 {
  font-style: italic;
}

.C1,
.D1,
.N {
  font-style: normal;
}</code>
<a href='#/' data-tag='header1'></a>
</details>
<hr>

<div class="header1">
  <span class="C1">LatestFooty.co.uk</span> is currently available for sale, along with:
  <div class="D1">
    LatestFootie.com<br> LatestFootie.co.uk
  </div>
</div>

<hr class='dash'>

<div class="header1">
  <span class="C2 N">LatestFooty.co.uk</span> is currently available for sale, along with:
  <div class="D2 N">
    LatestFootie.com<br> LatestFootie.co.uk
  </div>
</div>

<hr class='edge'>
<details><summary>Header 2</summary>
<u>Using :not() needs extra HTML tag:
Wrap second textnode in an inline or inline-block tag
As content of a descendant tag, the text can be targeted</u></details>
<details><summary>CSS</summary>
<code>.header2 *:not(.E):not(.F) {
  font-style: italic;
}</code>
<a href='#/' data-tag='header2'></a>
</details>
<hr>

<div class='header2'>
  <span class="E">LatestFooty.co.uk</span> <span>is currently available for sale, along with:</span>
  <div class="F">
    LatestFootie.com<br> LatestFootie.co.uk
  </div>
</div>

<hr class='edge'>
<details><summary>Header 3</summary>
<u>Smart solution with extra HTML tag:
Wrap second textnode in <code>&lt;i&gt;</code> or <code>&lt;em&gt;</code></u></details>
<details><summary>CSS</summary>
<code>.header3 {
  /* no styles needed */
}</code>
<a href='#/' data-tag='header3'></a>
</details>
<hr>

<div class='header3'>
  <span class="G">LatestFooty.co.uk</span> <i>is currently available for sale, along with:</i>
  <div class="H">
    LatestFootie.com<br> LatestFootie.co.uk
  </div>
</div>

<hr class='edge'>
<details><summary>Header 4</summary>
<u>Slickest solution with least HTML:
Wrap text that needs italics in <code>&lt;i&gt;</code> and then style lines with CSS</u></details>
<details><summary>CSS</summary>
<code>.header4 {
  white-space: pre-line;
}</code>
<a href='#/' data-tag='header4'></a>
</details>
<hr>

<header class='header4'>LatestFooty.co.uk <i>is currently available for sale, along with:</i>
  LatestFootie.com
  LatestFootie.co.uk
</header>
</main>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68