Home > Software design >  How to apply CSS style only if the last child is visible
How to apply CSS style only if the last child is visible

Time:01-20

I have this issue where I need to apply some style to a div only if it's next to a visible item (even if there's non visible items in between, but not if all the items following the current one are all invisible), as shown here:

https://stackblitz.com/edit/js-3nulnw?file=style.css

.container {
  display: flex;
  flex-grow: 1;
  padding: 5px;
}

.content {
  border: 1px solid #000000;
  border-radius: 4px;
  width: 50%;
  padding: 10px;
}

.addon {
  width: 10%;
  background-color: #ff0000;
  border: 1px solid #000000;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
  border-left: none;
}

.addon.hidden {
  display: none;
}

.content:not(:last-child),
.addon:not(:last-child) {
  border-right: none;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
<div >
  <div >This is shown correctly (no addons)</div>
</div>

<div >
  <div >This is shown correctly (one addon)</div>
  <div ></div>
</div>

<div >
  <div >
    This is shown correctly (multiple addons, one is hidden)
  </div>
  <div ></div>
  <div ></div>
</div>

<div >
  <div >This is shown correctly (multiple addons)</div>
  <div ></div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
  <div ></div>
</div>

I have tried looking on the internet but to no avail, but I'm probably looking for the wrong answer (most of what I found were other SO questions saying there's no CSS selector for non visible item), so alternative ways to do this are welcome (possibly without JS)

CodePudding user response:

Yes it is possible. I try to understand your question and get this result. Please expalin more your question https://developer.mozilla.org/en-US/docs/Web/CSS/:is

We can get it through (:is) pseudo-class function. we can check if div has class like visible in below example case then we can apply style to its next div throught ( ) adjacent sibling selector or (~) general sibling selector. I hope example will clear what I want to say

<div >
  <div >This is shown correctly (no addons)</div>
</div>

<div >
  <div >This is shown correctly (one addon)</div>
  <div ></div>
</div>

<div >
  <div >
    This is shown correctly (multiple addons, one is hidden)
  </div>
  <div ></div>
  <div ></div>
</div>

<!-- [ADD 'visible' CLASS BEFORE OF TARGET CONTAINER] -->
<div >
  <div >This is shown correctly (multiple addons)</div>
  <div ></div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
  <div ></div>
</div>

and its css will be

.container {
  display: flex;
  flex-grow: 1;
  padding: 5px;
}

.content {
  border: 1px solid #000000;
  border-radius: 4px;
  width: 50%;
  padding: 10px;
}

.addon {
  width: 10%;
  background-color: #ff0000;
  border: 1px solid #000000;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
  border-left: none;
}

.addon.hidden {
  display: none;
}

.content:not(:last-child),
.addon:not(:last-child) {
  border-right: none;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0; 
}


.container:is(.visible)   .container{
  background-color: rebeccapurple;
  color: white;
  /* Your style here */
}

.container:is(.visible)   .container .addon{
  background-color: brown;
  color: white;
  /* Your style here */
}

CodePudding user response:

I found a horrible but apparently effective way of achieving what I wanted

https://stackblitz.com/edit/js-4hkorq?file=style.css

.container {
  display: flex;
  flex-grow: 1;
  padding: 5px;
}

.content {
  border: 1px solid #000000;
  border-radius: 4px;
  width: 50%;
  padding: 10px;
}

.addon {
  width: 10%;
  background-color: #ff0000;
  border: 1px solid #000000;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
  border-left: none;
}

.addon.hidden {
  /* display: none; */
  visibility: hidden;
  width: 0px;
}

.content:not(:last-child),
.addon:not(:last-child) {
  border-right: none;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.container > div:last-of-type.hidden::before {
  content: '';
  border: 1px solid #000000;
  border-radius: 4px;
  border-left: none;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  height: 100%;
  display: flex;
  width: 4px;
  visibility: visible;
  position: relative;
  top: -1px;
}

.container > .addon   div:last-of-type.hidden::before {
  background-color: #ff0000;
}
<div >
  <div >This is shown correctly (no addons)</div>
</div>

<div >
  <div >This is shown correctly (one addon)</div>
  <div ></div>
</div>

<div >
  <div >
    This is shown correctly (multiple addons, one is hidden)
  </div>
  <div ></div>
  <div ></div>
</div>

<div >
  <div >This is shown correctly (multiple addons)</div>
  <div ></div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
</div>

<div >
  <div >This should have all rounded borders</div>
  <div ></div>
  <div ></div>
</div>

The trick is to use visibility: hidden and width: 0 instead of display: none, then add a ::before pseudo element that adds the border. It has the drawback of adding 4 pixels to the element (in order to have a 4 pixels border radius it needs to be at least 4 pixels wide) but it is acceptable in my case and (albeit with some tweaks) it also worked in my application. For some reason I had to add top: -1px because it wouldn't align otherwise, but in my application I didn't need to do

CodePudding user response:

These are the major changes to CSS

.container {display: table; table-layout: fixed; width: 100%...}
.addon {display: table-cell; position: relative; right: 5px; 
       z-index: 1; padding-right: 5px;...}
.addon .addon {right: 10px; padding-right: 10px;...}
.content {display: table-cell;} 

Since this is a pure CSS solution, the tags were changed so that :*-type-of works effectively. If you want a uniform height, add a div inside .content and give it an absolute height (ie px)

section:first-of-type > div {
  border-top: 1px solid #000000;
}

section:last-of-type > div {
  border-bottom: 1px solid #000000;
}

.container {
  display: table;
  table-layout: fixed;
  width: 100%;
}

.content {
  display: table-cell;
  padding: 15px;
  width: 50%;
  border-left: 1px solid #000000;
  border-right: 1px solid #000000;
  border-top: 0.5px solid #000000;
  border-bottom: 0.5px solid #000000;
  border-radius: 4px;
}

.addon {
  display: table-cell;
  width: 10%;
  position: relative;
  right: 5px;
  padding-right: 5px;
  z-index: 1;
  background-color: #ff0000;
  border-left: 0;
  border-right: 1px solid #000000;
  border-top: 0.5px solid #000000;
  border-bottom: 0.5px solid #000000;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.addon.hidden {
  display: none;
}

.addon .addon {
  right: 10px;
  padding-right: 10px;
}
<section >
  <div >This is shown correctly (no addons)</div>
</section>
<section >
  <div >This is shown correctly (one addon)</div>
  <aside ></aside>
</section>

<section >
  <div >
    This is shown correctly (multiple addons, one is hidden)
  </div>
  <aside ></aside>
  <aside ></aside>
</section>

<section >
  <div >This is shown correctly (multiple addons)</div>
  <aside ></aside>
  <aside ></aside>
</section>

<section >
  <div >This should have all rounded borders</div>
  <aside ></aside>
</section>

<section >
  <div >This should have all rounded borders</div>
  <aside ></aside>
  <aside ></aside>
</section>

  •  Tags:  
  • Related