Pure CSS collapse/expand div
Asked Answered
F

5

127

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;
}
Frias answered 26/2, 2013 at 17:54 Comment(2)
To any one seeing this not using wordpress can I please say PLEASE DONT DO THIS. Its a hack and it breaks the back functionality of a page which is super annoying.Metaphrase
@gbtimmon, why is this a hack if we aren't going to use wordpress?Harrison
A
153

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.

Amass answered 1/4, 2013 at 11:12 Comment(7)
Bug 591737 is the bug that tracks support in Firefox.Sanitarium
It's almost mid 2016 and support for this is nonexistent. Definitely don't use this solution.Base
@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 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 defaultAmass
@Amass 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.Base
I'm developing for modern browsers only and this technique worked perfectly for my use case, and didn't require a lick of CSS!Wondrous
@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.Amass
It's almost end of 2023 and support for this is extended. You can use this solution.Conformation
N
68

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>
Nuncupative answered 19/8, 2016 at 13:35 Comment(5)
<summary> and <details> still don't work in all major browsers.Chary
@TranslucentCloud That's what I said. ;)Nuncupative
And also this was said by the other answer's author.Chary
Is there anyway to add transition animation to this?Justin
For animated version where text slides down and fades in using CSS transition, see this blogpostAegisthus
C
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>
Chary answered 11/8, 2016 at 20:39 Comment(5)
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?Benavidez
Update: @Nuncupative 's CSS only answer below ("Pure CSS3", similar CSS to this, but using classes). does work with multiple divs on the same page.Benavidez
@Benavidez 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.Chary
Would be an awesome answer if it had a little more explanation, for example about what the ~ does.Epizoon
~ is a "general sibling combinator" which allows you to select sibling elements, that is those on the same level.Chary
M
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.

Metaphrase answered 26/2, 2013 at 18:16 Comment(1)
Thank you! I had tried that, but for some reason it wouldn't work for me. Must have had a bug i never caught.Frias
I
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/

Interwork answered 26/2, 2013 at 18:20 Comment(2)
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.Interwork
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.Frias

© 2022 - 2024 — McMap. All rights reserved.