Pure CSS collapsible tree menu

Jul 14
 
Pure CSS collapsible tree menu

The classic tree view, we all know it, it’s used everywhere and it definitely can be useful in the right context. I’ve seen various examples about doing it with CSS and they’ve all required JavaScript. Not content with any of those solutions I investigated doing it with pure CSS, I got a good head start from my Custom Radio and Checkbox inputs article. From there I’ve come up with a solution that works pretty well.

 

Another demo, another bug

Everything I seem to investigate lately seems to present itself with an annoying bug/feature in various browsers. Last time it was the inconsistency between browsers and generated content on form elements. This time it is WebKit not being able to apply styles using the checked pseudo-class in conjunction with a general sibling combinator (E ~ F) or chained adjacent sibling combinator (E + F + F). Making it very hard, and probably the reason I haven’t seen a CSS solution that works in WebKit browsers. I did come across this demo but due to the bug mentioned above doesn’t work in WebKit browsers.

So I soldiered on and came up with a pretty decent attempt, and remember folks I’m not a designer so be kinder this time with design critiques all I’m doing is showing you how to do the technique ;). With that out of the way let’s dig into the inner workings and road blocks I faced.

General sibling combinators are flaky

The CSS3 selector module has a very useful addition to compliment the CSS2.1 adjacent sibling combinator. Unlike the adjacent selector the general selector gives us some flexibility in that it will match a sibling that isn’t immediately preceded by our first element.

Great, I have 3 elements an <input>, <label> & <ol> the general sibling combinator is the perfect tool to do things like input:checked ~ ol. Check Firefox, awesome works, Opera too! Woo! Surely WebKit will have this…nope nothing. Let me try input ~ ol, yep works across the board, *face palm*.

So I dug into WebKits bug tracker and came out with this bug which has been around since 2007. Stating that general sibling combinators in combination with dynamic CSS, ala :checked, won’t reflect changes. Nor will it do chained adjacent combinator which was going to be my next solution.

However doing :checked with a single adjacent sibling combinator works fine in post 2008 WebKit browsers. So using this information I went about and built a working demo that has good browser support.

The demo is built using an ordered list (ol) nested with further ordered lists to naturally represent a basic folder structure.

<ol>
    <li class="file"><a href="document.pdf">File 1</a></li>
    <li>
        <label for="subfolder1">Subfolder 1</label>
        <input type="checkbox" id="subfolder1" />
        <ol>
            <li class="file"><a href="">File 2</a></li>
            <li class="file"><a href="">File 2</a></li>
            <li class="file"><a href="">File 2</a></li>
        </ol>
    </li>
</ol>

As you can see, in order to get around the general combinator issue in WebKit based browsers I have switched the label to come first then the input so the “folders” could be expanded/collapsed by checking/unchecking the checkbox.

li input {
    position: absolute;
    left: 0;
    margin-left: 0;
    opacity: 0;
    z-index: 2;
    cursor: pointer;
    height: 1em;
    width: 1em;
    top: 0;
}
li label {
    background: url(folder-horizontal.png) 15px 1px no-repeat;
    cursor: pointer;
    display: block;
    padding-left: 37px;
}

To sit the input and label in the right visual order I absolutely positioned the input and applied a left padding to the label to push it out. That way changing the styles of the child ol, when the input is checked, can be done using the adjacent sibling combinator. I’ve also set the cursor to pointer when hovered over the input or label to visual show they’re clickable.

li input + ol {
    background: url(toggle-small-expand.png) 40px 0 no-repeat;
    margin: -0.938em 0 0 -44px; /* 15px */
    display: block;
    height: 1em;
}

Unlike my custom radio and checkbox article where I added the background image on the label, this time I had to do some trickery and apply it to the direct sibling ol of an input. Applying a sprite image to the ol wouldn’t be possible in this situation due to it being applied to the ol which would make it difficult to effectively hide other images within the sprite image.

To position the ol correctly I use a negative margin to pull it into the right location so it will sit next to the label and underneath the invisible checkbox.

li input + ol > li {
    display: none;
    margin-left: -14px !important;
    padding-left: 1px;
}

To hide the sub folders so they don’t appear when the parent folder is collapsed I target the child list items and set them to, a zero height and hide any overflowAndy pointed out that I could just use display: none over height, this also stops the keyboard navigation from tabbing into non expanded items as they’re now hidden.

li label {
    background: url(folder.png) 15px 1px no-repeat;
}
li.file a {
    background: url(document.png) 0 -1px no-repeat;
    color: #fff;
    padding-left: 21px;
    text-decoration: none;
    display: block;
}

To differentiate between folders and files I applied a background image to either the label or to an anchor within a list item for files.

li {
    position: relative;
    margin-left: -15px;
    list-style: none;
}
li.file {
    margin-left: -1px !important;
}

To pull out the folder list items I apply a larger negative margin so the folder will line up with any of the file icons, and for file based list items I reset the left margin so they sit flush.

Change icon based on file extension

With some CSS3 attribute selectors we can determine an anchor links file format and change the icon accordingly.

li.file a[href $= '.pdf']     { background-position: -16px -1px; }
li.file a[href $= '.html']    { background-position: -32px -1px; }
li.file a[href $= '.css']     { background-position: -48px -1px; }
li.file a[href $= '.js']      { background-position: -64px -1px; }

Using the $= CSS attribute selector allows us to check the end of an attribute exactly ends in .pdf, .html etc.

If for some reason you attribute doesn’t end with your file extension, your anchor may have a query string on the end. We can still match file types.

li.file a[href *= '.pdf']   { background-position: -16px -1px; }
li.file a[href *= '.html']  { background-position: -32px -1px; }

The *= will match a substring that contains .pdf or .html anywhere within the attribute and if a href has a query string we can still match our file extension without issue. This does have the slight disadvantage that it will match it anywhere e.g. if you have a file called file.html.pdf it will match both file types and the one with the higher CSS specificity will be applied or incase of the example above their CSS specificity is the same so the html background will be applied.

Checkbox attributes

In the demo by default the first folder is open, this is done by adding the checked attribute to the checkbox which will trigger our styles thanks to the checked pseudo-class and reveal its sub files and folders.

We can also add the disabled attribute to the checkbox to stop a user from opening a folder as the input can neither be checked or unchecked.

Lastly using a combination of both disabled and checked will allow us to reveal the sub files and folders but not allow the user to close the top level folder.

Browser support

Based on testing this will work in any CSS3 selector supporting browser. The following have been tested and known to work

  • Firefox 1+
  • Opera 9.6+
  • Safari 4+
  • iPhone/iPod Safari
  • Chrome 1+
  • Android
  • IE9+

This could very well work in IE8 but would require some JavaScript to get IE8 to interpret the checked pseudo-class, which I won’t be going into.

Right now I use conditional comments to hide the stylesheet from all versions of IE and another conditional comment to load the stylesheet for IE9 and greater.

<!--[if gte IE 9 ]>
    <link rel="stylesheet" type="text/css" href="_styles.css" media="screen">
<![endif]-->
<!--[if !IE]>-->
    <link rel="stylesheet" type="text/css" href="_styles.css" media="screen">
<!--<![endif]-->

Highly scalable

This technique will cater for a large amount of sub folders and files. It’s governed by your screen real estate and even then it’ll apply scroll bars to the document when the tree structure gets too long or wide.

Any questions/comments/suggestions leave a comment.

Short URL: http://cssn.in/ja/026

 
 
 

Post filed under: css.

Skip to comment form.

Comments

  1. I was just looking at the options for tree structures in Javascript, and there are problems with all of them. This solution is flexible and elegant– thanks!

    Ari, July 15th, 2010
  2. i’ ever make it on my blog but i change it..thank’s for comin

    hendro-prayitno, July 15th, 2010
  3. It works here (Firefox 3.6.6 on windows), but with a small bug. “Folder 1″ is always expanded, even after reloading the page. It can’t be collapsed.

    b0rsuk, July 15th, 2010
  4. @b0rsuk – That’s the intended behaviour although I didn’t style it to indicate that it can’t be closed. The checkbox for the first folder has the checked and disabled attribute on it so that’s why it’s always open and can’t be closed.

    The Css Ninja, July 16th, 2010
  5. Very nice way of thinking you got going here, great work! Not sure how/if this should be used in a production environment though…?

    Pim Derks, July 18th, 2010
  6. We are using a collapsible tree in on of our websites. It is a bit slow as the number of nodes grows. We will try to put in practice what we learned with your article … many thanks!

    Ignacio Roda, July 20th, 2010
  7. Looks great but I couldn’t get it to work in IE8.

    bridget, August 27th, 2010
  8. @Bridget – That’s because, as stated in the article, it doesn’t work in IE8- as it doesn’t support the :checked pseudo-class. However it does work in IE9. Try Keith Clarks ie-css3 script to get it working in IE8 and down.

    The Css Ninja, August 27th, 2010
  9. I included selectivizr.js from http://selectivizr.com/ and it seems to work even in IE8.

    Warren, September 10th, 2010
  10. Nice tut, i cant think with css i can make tree menu. :) :)

    Om Ipit, October 4th, 2010
  11. Recently i have just try out this amazing tree but infact find out more compatible and easy to learn and use as well…Thanx css ninja ………………….!!!!!!!!!!!!!!!!

    MIlan K.B., October 12th, 2010
  12. Sweet man, do more iphone stuff i love it :D

    John, January 1st, 2011
  13. Excellent, thank you for all the effort to provide a Javascript-free, cross-browser-compatible result.

    Marios Zindilis, January 9th, 2011
  14. Hi.
    checked out the demo and its cool. was wondering how to get it up and running without the folder i.e to use it as a side menu on a website.
    will appreciate if you could put me through or mail it to me

    thanks and thumbs up

    Zeus, January 27th, 2011
  15. @Zeus
    Just reduce the padding on the label and remove the background reference. Same for the links.

    The Css Ninja, January 27th, 2011
  16. I am using this awesome coding in a wordpress site, but it just keeps coming up with the actual check boxes and not the tree. Any hinters?

    Matthew, January 28th, 2011
  17. @Matthew
    Make sure the images are loading and that the checkboxes are being targeted correctly.

    The Css Ninja, January 29th, 2011
  18. I got the css to load and the images are now loading. The next issue I have is that the expand and contract buttons are now off center. When I try to move them via css, they disappear.

    Matthew, February 1st, 2011
  19. Thanks

    salman, February 2nd, 2011
  20. I can confirm that this is working on my Palm Pre running WebOS 1.4.5.

    Franky W, February 5th, 2011
  21. Add
    transition: all 0.9s ease; -moz-transition: all 0.9s ease; -webkit-transition: all 0.9s ease; -o-transition: all 0.9s ease; -khtml-transition: all 0.9s ease;
    and you’ll get slowly expanding tree. To have more reasonable effect you can replace “all” with specific attributes.
    Thanks!

    Snusmumric, February 6th, 2011
  22. thankyou for this excellent tutorial was exactly what i was looking for would you know how to make the folders automatically collapse?

    dellboy, February 9th, 2011
  23. @Mathew – Strip it all back to raw functionality. I would say something is interfering the styles.

    The Css Ninja, February 9th, 2011
  24. The article has taught me a few new stuffs about CSS. Thanks.

    Techno, March 7th, 2011
  25. Hi! I really like this menu and want to use it on a site I am developing.
    http://www.piratespress.com/pages/

    ALL browsers load it great, except IE8. — it adds check boxes and loses all functionality.

    I tried adding the above IE8 hack to my style.css file, but it still looks crappy. Any ideas on what I can try?

    Jim, May 26th, 2011
  26. @Jim

    IE8 doesn’t support CSS3 and it states in the article that is not supported in IE8<. However you can add support by using selectivzr, including that in your page will make it work but you’ll also need to update the opacity CSS that hides the checkbox since IE8 doesn’t support opacity you’ll need to use the propriety filter property.

    Ryan Seddon, May 27th, 2011
  27. Thanks! You completely RULE!
    I got it working. Thanks again and great tutorial/support!

    Jim, May 27th, 2011
  28. I had implemented a jQuery file tree, but the code is hefty at a whopping 70k, and the HTML doesn’t exist without script, it’s difficult to configure and it won’t work on a wide plethora of devices.

    This is miles ahead, and I’m totally going to run with it on my site.

    Everyone knows the file tree menu system, and it’s great for whole-site navigation over the filesystem, provided you have a way to hide files effectively.

    Daniel, August 5th, 2011
  29. It works! Thanks. Wanted this to work on mobile phones without javascript. Need it for technicians accessing a knowledge base.

    How can I get a “collapse all” button using your method?

    Thanks

    Murray

    Murray Wiseman, August 19th, 2011
  30. Thanks

    Piranavan, August 23rd, 2011
  31. I couldn’t run this on Internet Explorer 7
    Why???

    Piranavan, August 23rd, 2011
  32. Hmm…

    Next question:
    How to integrate your lovely menu with “_self” in a website???

    : P

    Alex V, October 2nd, 2011
  33. Thanks for this. Is this licensed? If so, how is it licensed? Any problem if I use bits of it in a product?

    Paul, October 19th, 2011
  34. Hi Ryan,
    we were looking at your collapsible tree yesterday with regards to how well it worked with screen readers. The one condition that confused them was when a child folder was left open and a parent folder was collapsed. In that case you would want to hide both the parent and children records not just the parents records.

    e.g.

    Visible
    >Hidden
    >>Visible
    >>Visible

    would need to become

    Visible
    >Hiden
    >>Hidden
    >>Hidden

    Cheers,

    Andy

    Andy from Workshopshed, October 21st, 2011
  35. @Andy -

    Thank you Andy for the great info, would it make sense to a screen reader user if when a parent is closed but a child is still checked that all I’d do is set the height of any child containers to 0?

    e.g.

    Visible
    >Hidden
    >>Visible >Visible <– height: 0

    If that's an option I can then keep this solution as CSS only otherwise javascript would need to intervene to uncheck any child checkboxes of a hidden parent.

    Ryan Seddon, October 23rd, 2011
  36. @Paul

    I have a comment in more recent demo’s I have been doing linking off to this license http://thecssninja.com/demo/license.txt this applies to all demos unless stated otherwise. So you’re free to use it anyway you like.

    Ryan Seddon, October 23rd, 2011
  37. Hi there

    Great script, I’m using it on a site I’m developing for a friend. I’ve sort of adapted the system to use it as a set of links that drop down. I was wondering, if I wanted to put 2 versions of the menu on the same page, how would I change the CSS accordingly? As at the moment if I just add another tree on the page () things don’t work correctly – some menu’s expand while others don’t.

    Thanks.

    Patrick, October 25th, 2011
  38. thank you ryan for great tutorial!!! instead of menu im using it for collapse/expand text. I also used js for IE8, so its perfect. The only thing i wanna learn, please explain if you have time:

    li input + ol
    {
        background: url(toggle-small-expand.png) 40px 0 no-repeat;
        margin: -0.938em 0 0 -44px; /* 15px */
        xdisplay: block;
        height: 1em;
    }
    

    what is xdisplay property?

    Nina, October 30th, 2011
  39. @Nina

    Lol, that’s actually just a mistake the CSS parser will drop the property from the decleration. I usually just put a x in front of a property name so I can quickly switch off styles when debugging issues.

    Ryan Seddon, October 31st, 2011
  40. @Patrick

    If you have two versions on the one page you shouldn’t have to change anything, other than making sure the 2nd version doesn’t share any of the same id values as the first otherwise it’ll intefere with how the the checkboxes work.

    Ryan Seddon, October 31st, 2011
  41. Wow, I was thinking this one was with a little bit of javascript but you’ve impressed me!
    It’s truely pure CSS, very nice work.

    Eric, November 5th, 2011
  42. excellent post

    Can i spread this post in blogger blog giving a backlink to your post.

    gkr, November 6th, 2011
  43. Ryan, I’m not an expert on screen readers, the chap who was giving a talk said he tested your menu with NVDA and the zero height rows were indeed not read by that reader. Is there a reason why you use zero height and not hidden?

    Andy from Workshopshed, November 8th, 2011
  44. Just tried navigating using just the keyboard, tabbing and using space to toggle the checkboxes works but you can tab to items that are not visible.

    Andy from Workshopshed, November 8th, 2011
  45. @Andy

    That’s a very good point I could just use display: none/block over height I’m not sure why I did it originally, but I’ve updated the demo to use that. This also fixes the issue for keyboard navigation. They’re now hidden so you can’t tab to hidden elements unless they’re a visible so it works much more logically now.

    Cheers!

    Ryan Seddon, November 9th, 2011
  46. Thanks Ninja, that’s goooood

    mehran, November 15th, 2011
  47. I used to use “joust” in my old days in school… but I find your solution very very impressive. I’m a HW guy and NOT a SW guy at all. So, I have a very silly question, basically I want the tree to stick to the left side of the page, and my files to show up on right side, …

    This is probably as per previous query by one of the users…

    @Zeus
    Just reduce the padding on the label and remove the background reference. Same for the links.

    Can you please provide an example of this? Where do I reduce “padding” and remove “background reference”?

    I’m again sorry for silly question…

    Jag, November 16th, 2011
  48. Love the tree!

    I can’t get the selectivizr to work, though. I’ll admit I’m a total newbie, so I think the problem is simply that I don’t know how to correctly enter the reference to the javascript library or how to make sure Dreamweaver CS5 has one, or…

    So I’m sure it’s a really stupid question, but how do I find currently existing javascript libraries, and how do I tell the script/page to reference them?

    Thanks!

    Erin, December 31st, 2011
  49. I use it as an index in the Archive section of my photo blog. This is perfect. Absolutely perfect. Thanks Css Ninja! And, it validates Strict, too! Just ideal for my site!

    Wayne B, January 21st, 2012

Trackbacks

  1. [...] CSS Ninja has created an amazing Pure CSS collapsible tree menu. [...]

    The Great Geek Manual » Link Round-Up: July 14, 2010, July 14th, 2010
  2. [...] Więcej: Pure CSS collapsible tree menu | The CSS Ninja – All things CSS … [...]

    Pure CSS collapsible tree menu | The CSS Ninja – All things CSS … : : css, July 14th, 2010
  3. [...] Pure CSS collapsible tree menu | The CSS Ninja – All things CSS, Javascript & xhtmlthecssninja.com [...]

    Pure CSS collapsible tree menu | The CSS Ninja – All things CSS, Javascript & xhtml « Netcrema – creme de la social news via digg + delicious + stumpleupon + reddit, July 15th, 2010
  4. [...] original post here: Pure CSS collapsible tree menu | The CSS Ninja – All things CSS … This entry was posted on Wednesday, July 14th, 2010 at 11:52 am and is filed under [...]

    Pure CSS collapsible tree menu | The CSS Ninja – All things CSS … | My Blog, July 15th, 2010
  5. === popurls.com === popular today…

    yeah! this story has entered the popular today section on popurls.com…

    === popurls.com === popular today, July 15th, 2010
  6. [...] more here: Pure CSS collapsible tree menu | The CSS Ninja – All things CSS, Javascript & xhtml 14 July 2010 | Uncategorized | Trackback | del.icio.us | Stumble it! | View Count : 0 Next Post [...]

    Pure CSS collapsible tree menu | The CSS Ninja – All things CSS, Javascript & xhtml : Popular Links : eConsultant, July 15th, 2010
  7. [...] This post was Twitted by IMBild [...]

    Twitted by IMBild, July 18th, 2010
  8. [...] This post was Twitted by write2binoy [...]

    Twitted by write2binoy, July 18th, 2010
  9. [...] Pure CSS collapsible tree menu | The CSS Ninja – All things CSS, Javascript & xhtml – August 4th %(postalicious-tags)( tags: css menu webdesign tree navigation webdev javascript development )% [...]

    Delicious Bookmarks for August 4th from 18:05 to 18:13 « Lâmôlabs, August 5th, 2010
  10. [...] [...]

    50 Refreshing CSS Tutorials, Techniques and Resources | Prabaharan CS Blog, August 30th, 2010
  11. [...] Pure CSS Collapsible Tree Menu [...]

    50 Refreshing CSS Tutorials, Techniques and Resources « UKWDS!, August 31st, 2010
  12. [...] Pure CSS Collapsible Tree Menu [...]

    50 Refreshing CSS Tutorials, Techniques and Resources | Programming Blog, September 8th, 2010

Leave a comment