Cengiz Cengiz - 1 month ago 6
CSS Question

How to keep pure-HTML-&-CSS collapsible panels open when another one is opened?

I have the following collapsible panels written in pure HTML and CSS:



<style>
.collapse-panel {
display: block;
vertical-align: top;
height: auto !important;
}

.collapse-panel a {
font-family: "Lucida Console", Monaco, monospace;
font-size: 16pt;
font-weight: bolder;
text-decoration: none;
}

.collapse-panel-content {
display: none;
height: auto;
}

.show {
display: none;
}

.hide:target + .show {
display: inline;
}

.hide:target {
display: none;
}

.hide:target ~ .collapse-panel-content {
display: inline;
}
</style>
<div>
<div>
<div class="collapse-panel">
<a href="#hide1" class="hide" id="hide1">+</a>
<a href="#show1" class="show" id="show1">-</a>
<span>Details A</span>
<div class="collapse-panel-content">
<div>Some details</div>
</div>
</div>
</div>
</div>

<div>
<div>
<div class="collapse-panel">
<a href="#hide2" class="hide" id="hide2">+</a>
<a href="#show2" class="show" id="show2">-</a>
<span>Details B</span>
<div class="collapse-panel-content">
<div>Some other details</div>
</div>
</div>
</div>
</div>





How can I adapt the code so that the other collapsible panels are not closed when one is opened?

Answer

I don't think you can, using :target.

From w3schools:

The :target selector can be used to style the current active target element.

And that's exactly what you are doing. Now, can you think of a way to have more than one :active element at a time? Because I don't know any.

But that doesn't mean you can't achieve this with CSS alone. You just need to be creative. Use elements that are capable of natively storing the current element state while exposing it to CSS selectors. For example, checkboxes:

[id^="collapseCheck-"]{
  display: none;
}
label[for^="collapseCheck-"] {
  display: block;
}
label[for^="collapseCheck-"]:before {
  content: "+";
  width: 1rem;
  text-align: center;
  display: inline-block;
}

[id^="collapsePanel-"] {
  display: none;
  padding: 1rem;
}

#collapseCheck-1:checked ~ label[for="collapseCheck-1"]:before {content: "-"}
#collapseCheck-2:checked ~ label[for="collapseCheck-2"]:before {content: "-"}
#collapseCheck-3:checked ~ label[for="collapseCheck-3"]:before {content: "-"}
/*...*/

#collapseCheck-1:checked ~ #collapsePanel-1 {display: block;}
#collapseCheck-2:checked ~ #collapsePanel-2 {display: block;}
#collapseCheck-3:checked ~ #collapsePanel-3 {display: block;}
/*...*/
<input type="checkbox" id="collapseCheck-1">
<input type="checkbox" id="collapseCheck-2">
<input type="checkbox" id="collapseCheck-3">
<!-- ... -->


<label for="collapseCheck-1">Details A</label>
  <div id="collapsePanel-1">
    <div>Some details</div>
  </div>

<label for="collapseCheck-2">Details B</label>
  <div id="collapsePanel-2">
    <div>Some details</div>
  </div>

<label for="collapseCheck-3">Details C</label>
  <div id="collapsePanel-3">
    <div>Some details</div>
  </div>

<!-- ... -->

You can generate the required list of CSS selectors using a foreach on server side, if your elements are dynamic. The limitation here is that all your control checkboxes need to be placed at the same level as the list of items (or you need to adjust the selector, of course) and they need to be placed before your elements (CSS only looks ahead, never behind).

This solution can be easily switched to "close all the other containers" by switching the type of control <input>s to radios sharing the same name attribute.


Please note this is a proof of concept. In order to make it usable in a production level environment, one should also add aria attributes to it, so it would becomes accessible and usable by all devices, not only screen types.

Comments