122

I have a pure CSS collapsable div which is based on someone else's code who uses the :target psuedoclass. What I am trying to set up is a page with 12+ questions, and when you click on the + button the answer div expands beneath. I cannot figure out how to make multiple collapsing div elements on this page without writing a ton of extra CSS. Anyone have suggestions on how to write this so my CSS code is minimized? (i.e., so i dont have to input a bunch of unique selectors for each of the 12+ questions).

I cannot use Javascript since this is going on a wordpress.com site which does not allow JS.

Here is my jfiddle: http://jsfiddle.net/dmarvs/94ukA/4/

<div class="FAQ">
    <a href="#hide1" class="hide" id="hide1">+</a>
    <a href="#show1" class="show" id="show1">-</a>
    <div class="question"> Question Question Question Question Question Question Question Question Question Question Question? </div>
        <div class="list">
            <p>Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer Answer </p>
        </div>
</div>
/* source: http://www.ehow.com/how_12214447_make-collapsing-lists-java.html */

.FAQ { 
    vertical-align: top; 
    height:auto !important; 
}
.list {
    display:none; 
    height:auto;
    margin:0;
    float: left;
}
.show {
    display: none; 
}
.hide:target + .show {
    display: inline; 
}
.hide:target {
    display: none; 
}
.hide:target ~ .list {
    display:inline; 
}

/*style the (+) and (-) */
.hide, .show {
    width: 30px;
    height: 30px;
    border-radius: 30px;
    font-size: 20px;
    color: #fff;
    text-shadow: 0 1px 0 #666;
    text-align: center;
    text-decoration: none;
    box-shadow: 1px 1px 2px #000;
    background: #cccbbb;
    opacity: .95;
    margin-right: 0;
    float: left;
    margin-bottom: 25px;
}

.hide:hover, .show:hover {
    color: #eee;
    text-shadow: 0 0 1px #666;
    text-decoration: none;
    box-shadow: 0 0 4px #222 inset;
    opacity: 1;
    margin-bottom: 25px;
}

.list p{
    height:auto;
    margin:0;
}
.question {
    float: left;
    height: auto;
    width: 90%;
    line-height: 20px;
    padding-left: 20px;
    margin-bottom: 25px;
    font-style: italic;
}
John Slegers
  • 45,213
  • 22
  • 199
  • 169
dmarvs
  • 1,390
  • 2
  • 9
  • 5

5 Answers5

149

Depending on what browsers/devices you are looking to support, or what you are prepared to put up with for non-compliant browsers you may want to check out the <summary> and <detail> tags. They are for exactly this purpose. No css is required at all as the collapsing and showing are part of the tags definition/formatting.

I've made an example here:

<details>
<summary>This is what you want to show before expanding</summary>
<p>This is where you put the details that are shown once expanded</p>
</details>

Browser support varies. Try in webkit for best results. Other browsers may default to showing all the solutions. You can perhaps fallback to the hide/show method described above.

ricksmt
  • 888
  • 2
  • 13
  • 34
Thurstan
  • 1,626
  • 1
  • 9
  • 6
  • 2
    [Bug 591737](https://bugzilla.mozilla.org/show_bug.cgi?id=591737) is the bug that tracks support in Firefox. – ShreevatsaR May 30 '14 at 06:20
  • 11
    It's almost mid 2016 and support for this is nonexistent. Definitely don't use this solution. – Dwayne Charrington Apr 19 '16 at 10:43
  • 3
    @DigitalSea support being 'nonexistent' is incorrect and 'definitely don't use this solution' are strong words for a standard that has been stable for several years and has had a polyfill available since 2010 https://mathiasbynens.be/notes/html5-details-jquery. What is the basis for your argument? MS edge is likely to implement these tags soon Firefox has implemented summary/detail tags (but currently has it behind a flag) Webkit supports it as default since chrome 12 as does safari on OS X and iOS Blink derived browsers - chrome mobile/desktop, opera support it as default – Thurstan Apr 22 '16 at 02:28
  • 1
    @Thurstan No support in; IE8, IE9, IE10, IE11, Edge 13, no support in Firefox until August 2016. So unless you have the luxury of forgoing support of IE entirely and currently Edge and Firefox, yeah, not well-supported. The polyfill essentially does what you can already do with Javascript (without jQuery) and quite efficiently. A polyfill for this one easy to implement yourself feature is overkill. The solution you linked uses jQuery which is unnecessary. I would not be surprised if we see this removed from the spec one day anyway. – Dwayne Charrington Apr 22 '16 at 06:54
  • 8
    I'm developing for modern browsers only and this technique worked perfectly for my use case, and didn't require a lick of CSS! – vhs Feb 04 '17 at 09:26
  • 1
    @joshh glad to be of help. Support seems to be finally arriving for this useful element. It is apparently in development in edge and Firefox now supports it natively. It is part of the w3c recommendation spec. – Thurstan Feb 05 '17 at 12:04
65

Using <summary> and <details>

Using <summary> and <details> elements is the simplest but see browser support as current IE is not supporting it. You can polyfill though (most are jQuery-based). Do note that unsupported browser will simply show the expanded version of course, so that may be acceptable in some cases.

/* Optional styling */
summary::-webkit-details-marker {
  color: blue;
}
summary:focus {
  outline-style: none;
}
<details>
  <summary>Summary, caption, or legend for the content</summary>
  Content goes here.
</details>

See also how to style the <details> element (HTML5 Doctor) (little bit tricky).

Pure CSS3

The :target selector has a pretty good browser support, and it can be used to make a single collapsible element within the frame.

.details,
.show,
.hide:target {
  display: none;
}
.hide:target + .show,
.hide:target ~ .details {
  display: block;
}
<div>
  <a id="hide1" href="#hide1" class="hide">+ Summary goes here</a>
  <a id="show1" href="#show1" class="show">- Summary goes here</a>
  <div class="details">
    Content goes here.
  </div>
</div>
<div>
  <a id="hide2" href="#hide2" class="hide">+ Summary goes here</a>
  <a id="show2" href="#show2" class="show">- Summary goes here</a>
  <div class="details">
    Content goes here.
  </div>
</div>
Amoliski
  • 160
  • 6
Wernight
  • 36,122
  • 25
  • 118
  • 131
34

@gbtimmon's answer is great, but way, way too complicated. I've simplified his code as much as I could.

#answer,
#show,
#hide:target {
    display: none; 
}

#hide:target + #show,
#hide:target ~ #answer {
    display: inherit; 
}
<a href="#hide" id="hide">Show</a>
<a href="#/" id="show">Hide</a>
<div id="answer"><p>Answer</p></div>
Maximilian Hils
  • 6,309
  • 3
  • 27
  • 46
Neurotransmitter
  • 6,289
  • 2
  • 51
  • 38
  • 1
    This doesn't work when used more than once on the same page: clicking hide / show affects all divs. Anyone know how to modify it such that it does work when used more than once? – phhu Jul 01 '20 at 21:51
  • Update: @Wernight 's CSS only answer below ("Pure CSS3", similar CSS to this, but using classes). does work with multiple divs on the same page. – phhu Jul 01 '20 at 22:04
  • 1
    @phhu yes, it won't work if you reuse the same IDs. It will work if you will introduce other set of IDs (`hide2`, `show2`, `answer2` for example). Or classes, it doesn't matter. – Neurotransmitter Jul 02 '20 at 15:03
  • Would be an awesome answer if it had a little more explanation, for example about what the `~` does. – not2savvy Apr 18 '23 at 12:29
  • `~` is a "general sibling combinator" which allows you to select sibling elements, that is those on the same level. – Neurotransmitter Apr 24 '23 at 00:04
23

You just need to iterate the anchors in the two links.

<a href="#hide2" class="hide" id="hide2">+</a>
<a href="#show2" class="show" id="show2">-</a>

See this jsfiddle http://jsfiddle.net/eJX8z/

I also added some margin to the FAQ call to improve the format.

gbtimmon
  • 4,238
  • 1
  • 21
  • 36
  • Thank you! I had tried that, but for some reason it wouldn't work for me. Must have had a bug i never caught. – dmarvs Feb 26 '13 at 21:01
19

Or a super simple version with barely any css :)

<style>   
.faq ul li {
    display:block;
    float:left;
    padding:5px;
}

.faq ul li div {
    display:none;
}

.faq ul li div:target {
    display:block;
}


</style>


<div class="faq">
   <ul>
   <li><a href="#question1">Question 1</a>   
   <div id="question1">Answer 1 </div>
   </li>


   <li><a href="#question2">Question 2</a>
   <div id="question2">Answer 2 </div>
   </li>
   <li><a href="#question3">Question 3</a>
   <div id="question3">Answer 3 </div>
   </li>
   <li><a href="#question4">Question 4</a>
   <div id="question4">Answer 4 </div>
   </li>
   <li><a href="#question5">Question 5</a>
   <div id="question5">Answer 5 </div>
   </li>
   <li><a href="#question6">Question 6</a>
   <div id="question6">Answer 6 </div>
   </li>
   </ul>  
</div>

http://jsfiddle.net/ionko22/4sKD3/

Ionko Gueorguiev
  • 302
  • 4
  • 18
  • By the way forgot to mention that :target doesn't work in IE8 or older, surprise surprise! And css PIE wo't help with that either so you may want to look at a jQuery option if browser compatibility is of interest to you. – Ionko Gueorguiev Feb 26 '13 at 18:22
  • Thank you, that is much cleaner. About a third of our potential target audience will be on IE8. I was thinking of just adding some conditional CSS which would just open all the divs on load. – dmarvs Feb 26 '13 at 21:02