Sunday, April 7, 2013

Very simple pure CSS collapsible list without JavaScript

Lately, I stumbled upon this great article on The CSS Ninja. It shows a technique for pure CSS collapsible lists, without any JavaScript. It works for all modern browsers. With Internet Explorer 8 or older though, it's not possible with pure CSS. These browsers need to emulate CSS pseudo-classes by adding a small JavaScript fix to the page, which I demonstrate at the bottom of this article.

The code on CSS Ninja was a bit too sophisticated for me. It took some time until I realized what's the magic behind it. I dismantled the code and simplified it as much as possible to understand it better. Here's how my simple collapsible list looks:

    • Item 1
    • Item 2
    • Item 3
    • Item 1
    • Item 2
    • Item 3

Note the checkboxes next to the list items. They are used as a switch to show and hide the list contents. We need them because without JavaScript, there's no variable to keep track of the visibility status of our list items. Don't worry at this point, we will hide the checkboxes later. I just left them visible to show you how it works.

CSS code

.collapsibleList li > input + * {
 display: none;
}
 
.collapsibleList li > input:checked + * {
 display: block;
}

.collapsibleList li > input {
 display: none;
}

.collapsibleList label {
 cursor: pointer;
}

HTML code

<ul class="collapsibleList">
 <li>
  <label for="mylist-node1">Click to open list 1</label>
  <input type="checkbox" id="mylist-node1" />
  <ul>
   <li>Item 1</li>
   <li>Item 2</li>
   <li>Item 3</li>
  </ul>
 </li>
 <li>
  <label for="mylist-node2">Click to open list 2</label>
  <input type="checkbox" id="mylist-node2" />
  <ul>
   <li>Item 1</li>
   <li>Item 2</li>
   <li>Item 3</li>
  </ul>
 </li>
</ul>

Now that's simple, isn't it? Any list with the collapsibleList class will become a collapsible list. This is the complete code, you don't need anything else. Read on for a look behind the scenes.

How it works

The magic happens at the <label> and <input> tags. The clickable list items look like ordinary links, but they are actually form element labels paired with a checkbox. Because they have the same ID, they are linked to each other, and clicking on a label will automatically tick its corresponding checkbox.

Note that each label and checkbox have to be paired with an unique identifier, like so:

<label for="mylist-node1" />
<input type="checkbox" id="mylist-node1" />

Here, mylist-node1 is the ID used to connect the label to the checkbox. It doesn't matter how you name the ID pairs, they just have to be unique across the page. If you don't pair them with an ID, the checkbox won't be ticked when you click on the label, because there is no semantic relationship between them.

Collapsing the list

.collapsibleList li > input + * {
  display: none;
 }

This code initially collapses our list by setting the first element following each checkbox to be invisible. In our example, the first element after each input element is another list, but it could be anything.

Expanding the list

.collapsibleList li > input:checked + * {
  display: block;
}

This is where the magic happens. We use the checked CSS pseudo-class for matching any ticked checkbox within our collapsible list. If it's ticked, we set the element immediately following the checkbox (in our example it's another list) to be visible.

Hiding the checkboxes

Now what about those ugly checkboxes, how to make them disappear? Just set their display property to none:

.collapsibleList li > input {
 display: none;
}

Done! This is how our finished list looks like:

    • Item 1
    • Item 2
    • Item 3
    • Item 1
    • Item 2
    • Item 3

CSS fix for IE 8 and older

As mentioned, the collapsible list will work with all modern browsers. However, it won't work out of the box with IE 8 or older. These browsers need a JavaScript CSS selector fix like ie7-js. Add this fix to your page to make it work.

For IE 7 or older, you additionally have to modify the above CSS code for hiding the checkboxes, because display: none won't work. As a workaround, we simply position the checkboxes outside of the body:

.collapsibleList li > input {
 position: absolute;
 left: 0;
 margin-left: -999px;
}

This code is kind of a hack, but it provides the highest compatibility. It works for all modern browsers and Internet Explorer 6, 7 and 8.

34 comments:

  1. How to not dsplay the check box?

    ReplyDelete
  2. I updated my blog post. At the bottom, I added an example where the checkboxes are hidden.

    ReplyDelete
  3. how can i add more lists?
    i add in the code but it appears already open.

    ReplyDelete
    Replies
    1. Did you make sure all the ID values are unique?

      Delete
  4. I checked the code, it should work just as shown here. See if the following test page works for you:

    http://jsfiddle.net/n6HXb/

    ReplyDelete
    Replies
    1. it does. my list is rtl. it worked before but i had to copy it to another page and suddenly it doesnt work. everything is same, html code and css. i cant figure out whats the problem.
      and thanks for the quick response =]

      Delete
    2. You can post a link to your page so I can check it, maybe it's just a small thing you forgot.

      Delete
    3. its not online yet..
      but i did find something in the css that was messing with the ul and li, i guess the problem is my css, ill search deep in there.
      thanks for helping, its a cool technique! :]

      Delete
    4. If you're using Chrome, you can press ctrl+shift+i and it will open the developer mode. Use the magnifying glass tool to select your list on the page, then inspect the CSS properties on the right. This is a good way to sort out CSS problems.

      Delete
    5. thanks! i solved it, a css property preventing from ul and li getting a margin.
      works like a charm :]

      Delete
    6. It doesn't work in IE8 even after using the ie7-js.Please help.

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. This works great in everything but IE8. So I added ie7-js to my HTML (I used IE9.js) and that got rid of the checkboxes, but the list does not collapse. My test is here:
    https://dl.dropboxusercontent.com/u/7260893/tree/tree.html

    ReplyDelete
    Replies
    1. Did you happen to make the collapsible list work anyhow?

      Delete
    2. Yes, if you check out this link in Chrome, Firefox or IE9+, it works great.
      https://dl.dropboxusercontent.com/u/7260893/tree/tree.html

      Delete
    3. So can you think of why it does not work in IE8 with ie7-js?

      Delete
    4. No, I gave up on getting it to work in IE8, and just added a conditional comment that says to use IE 9 or above, Firefox or Chrome.

      Delete
  8. Thanks for sharing! This is awsome! I just finished creating it and then viewed it in ie9 and it will not collapse. Is there a fix that you know of?

    ReplyDelete
    Replies
    1. It should work out of the box with ie9, I have tested it. Please check your syntax and if all else fails, consider adding the IE javascript fix I mentioned at the bottom of this page, although it shouldn't be necessary.

      Delete
  9. I tried it just using your code, but it will not work. Viewing this page, it works in IE just fine. I couldn't paste my code here, so I used a Google Doc to allow you to look at it. If you wouldn't mind taking the time, please let me know if I am missin gsomething.

    https://docs.google.com/document/d/1OG9m4yF9bLxQJC426vU-KDr65KGqFCgKsxck5lKKEE4/edit?usp=sharing

    ReplyDelete
    Replies
    1. Have you added a DOCTYPE directive to your document? IE needs this for CSS to work correctly. Try XHTML 1.0 Transitional.

      Delete
    2. Also, you must not use "display: none" for hiding the checkboxes with older IE versions. This will break the show/hide functionality. Only use the workaround code. You used "display: none", so your code won't work with older IE versions.

      Delete
  10. I finally got it working! The only issue I see now, is that the page jumps to the left as you click on a node item. If you have any ides on how to get rid of that, I would really appreciate it! Thakns so much for all of your help.

    ReplyDelete
  11. Excellent blog right here! Also your website loads up very fast! What web host are you the use of Can I get your affiliate hyperlink to your host I desire my web site loaded up as fast as yours lol

    ReplyDelete
  12. Great! Love the simplicity and cleanliness of this. Awesome job, thanks so much

    ReplyDelete
  13. How the "opened" views can be unviewed by clicking a new one?

    ReplyDelete
  14. I had been searching around for quite a while for something as easy and simple but yet useful like your example. It perfectly fits for me. Because I know very little about HTML let me ask: How could I create a button (or something else - clickable), which collapses/folds all collapsable list-entries?

    ReplyDelete
  15. Is this working in iOS devices?

    ReplyDelete
  16. Thanks! This came in handy for a directory I am creating for a church website. Quick and easy to use.

    ReplyDelete
  17. Looked everywhere for a simple solution like this! Thank you!

    ReplyDelete