A few people have been asking about the expandable lists I have in the sidebar, so I thought I’d share how I did this. The effect is actually quite easy to achieve and uses some simple JavaScript to change the CSS display property of the element containing the list. However, there are a few things to note.
collapsible-lists.zip21.7KB21st April 2013
Firstly, we can’t use the standard sidebar widgets. The lists have to be created using PHP and each list requires different PHP code.
Secondly, I wanted each list’s state, whether expanded or collapsed, to be persistent across page reloads. For example, when someone navigates to a different page. There are several ways to accomplish this, but I chose to use cookies that expire when the browser is closed. So, this adds a level of complexity to the solution.
Finally, it’s just worth mentioning that although I’m using JavaScript, it does degrade nicely if the user has JavaScript disabled in their browser.
As I mentioned, each list requires different PHP code to generate it. So to make this tutorial easier to follow I’ll focus on just the Categories list. At the end I’ll provide the code for the other lists I’m currently using in the sidebar.
Table of Contents
1. Generating the Categories List
As we’ll be executing PHP code directly from the sidebar, you first need to download and install the PHP Code Widget plugin for WordPress. Having done so, drag the PHP Code Widget to the sidebar.
The PHP code used to generate and display the Categories list is:
<?php echo wp_list_categories('title_li='); ?>
Note that the title_li argument is set to empty to prevent the default title, Categories from being displayed. We’ll set the list’s title using the <h3> tag.
If you copy and paste the above code into the PHP Code Widget, the Categories list will be included in the sidebar. But as is, we can’t really manipulate the list with JavaScript without first adding some HTML like so:
<!-- CATEGORIES --> <span> <h3 class="widgettitle ex-noline">Categories</h3> <span class="ex-link"> <a id="sidebar-categories-link" href="javascript:ExpandCollapse(document.getElementById('sidebar-categories'), 'Categories List')"> <span id="sidebar-categories-sign" class="ex-sign"></span> </a> </span> </span> <p class="ex-spacer"> </p> <div id="sidebar-categories" class="widget widget_categories"> <ul> <?php echo wp_list_categories('title_li='); ?> </ul> </div>
This may seem a little like overkill, but it’s necessary to both style the list and allow us to access elements with JavaScript.
Some points to note in this code which make it unique from the code we’ll use for other lists is of course the PHP code, but also the id of the <a> tag: sidebar-categories-link, the id of the <span> tag containing the Expand/Collapse link text: sidebar-categories-sign and the id of <div> tag containing the Categories list: sidebar-categories. Incidentally, sidebar-categories is also the name of the cookie that will be used to control the display of the Categories list.
2. Creating the JavaScript Functions
Now we can set-up our JavaScript functions to handle the expanding and collapsing of the lists. As mentioned, I’m using cookies so most of these functions are concerned with handling these.
function ExpandCollapse(e, title) { if (e.style.display=="none") { SidebarWidget(e.id, false, title, true); } else { SidebarWidget(e.id, true, title, true); } } function SidebarWidget(cookie_name, collapsed, title, set_cookie) { if (!collapsed) { if (set_cookie) { SetCookie(cookie_name, '', null); } ChangeSidebarWidget(document.getElementById(cookie_name), false, title); } else { if (set_cookie) { SetCookie(cookie_name, 'none', null); } ChangeSidebarWidget(document.getElementById(cookie_name), true, title); } } function ChangeSidebarWidget(e, collapsed, title) { if (!collapsed) { e.style.display = ''; document.getElementById(e.id + '-sign').innerHTML = ' [-]'; document.getElementById(e.id + '-link').setAttribute('title', 'Collapse ' + title); } else { e.style.display = 'none'; document.getElementById(e.id + '-sign').innerHTML = ' [+]'; document.getElementById(e.id + '-link').setAttribute('title', 'Expand ' + title); } } function IsCollapsed (cookie_name) { if (GetCookie(cookie_name) == '') { return false; } else { return true; } } function IsCookieSet (cookie_name) { if ( document.cookie.length == 0 ) { return false; } else { cookie_start = document.cookie.indexOf(cookie_name + "="); if ( cookie_start == -1 ) { return false; } else { return true; } } } function GetCookie(cookie_name) { cookie_start=document.cookie.indexOf(cookie_name + "="); cookie_start=cookie_start + cookie_name.length + 1; cookie_end=document.cookie.indexOf(";",cookie_start); if (cookie_end==-1) { cookie_end=document.cookie.length; } return unescape(document.cookie.substring(cookie_start,cookie_end)); } function SetCookie(cookie_name,value,expiredays) { var exdate=new Date(); exdate.setDate(exdate.getDate()+expiredays); document.cookie=cookie_name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires=" + exdate.toGMTString()) + "; path=/"; }
For clarity, I have these functions in a separate .js file in a sub-folder of the theme’s directory: custom/custom-user-scripts.js. So in the theme’s header.php file I need to include this line of code before the closing </head> tag.
<script type='text/javascript' src="<?php bloginfo('template_directory'); ?>/custom/custom-user-scripts.js"></script>
3. Initialising the Categories List
When the page first loads I want to be able to set an initial default value which determines if the Categories list is expanded or collapsed. The cookie that will subsequently control the display of the Categories list also needs to be set. The cookie will expire once the user has closed their browser window.
To do this, add the following JavaScript to the theme’s footer.php file just before the closing </body> tag:
<script type="text/javascript"> /* <![CDATA[ */ if ( !IsCookieSet('sidebar-categories') ) { // Initial display collapsed //SidebarWidget('sidebar-categories', true, 'Categories List', true); // Initial display expanded SidebarWidget('sidebar-categories', false, 'Categories List', true); } else { SidebarWidget('sidebar-categories', IsCollapsed('sidebar-categories'), 'Categories List', false); } /* ]]> */ </script>
You’ll notice on both line 6 and line 9 there is a call to SidebarWidget(). I’ve commented-out line 6 as only one of these is needed. I’ve included both so you can see how to set the initial display of the Categories list.
In the above example the Categories list will initially be expanded. This code is only executed once, when the page first loads and the associated cookie does not exist. On subsequent re-loads, whether the Categories list is expanded or collapsed depends on the value of the associated cookie and is handled by the call to the SidebarWidget() function on line 11. This line of code does not need to be changed.
4. Styling with CSS
Finally, we need to add some CSS selectors to style the Categories list. I added these to the end of my theme’s style.css file:
/* ------- Expand/Collapse Sidebar Lists ------- */ h3.ex-noline { display: inline; background-image: none; } span.ex-link { display: inline; font-size: .9em; } span.ex-sign { line-height: 3em; } p.ex-spacer { margin: 0; background:url(images/line.gif) repeat-x scroll center bottom; }
Please note that this CSS is specific to the theme I’m using. You may need to adjust the properties/values depending on your theme.
5. Code for Archives List
Place this code in a separate PHP Code Widget in the sidebar:
<!-- ARCHIVES --> <span> <h3 class="widgettitle ex-noline">Archives</h3> <span class="ex-link"> <a id="sidebar-archives-link" href="javascript:ExpandCollapse(document.getElementById('sidebar-archives'), 'Archives List')"> <span id="sidebar-archives-sign" class="ex-sign"></span> </a> </span> </span> <p class="ex-spacer"> </p> <div id="sidebar-archives" class="widget widget_categories"> <ul> <?php echo wp_get_archives('type=monthly&limit=15'); ?> </ul> </div>
External link: wp_get_archives()
Place this code in the theme’s footer.php file:
// ARCHIVES if ( !IsCookieSet('sidebar-archives') ) { SidebarWidget('sidebar-archives', false, 'Archives List', true); // Initial display is expanded } else { SidebarWidget('sidebar-archives', IsCollapsed('sidebar-archives'), 'Archives List', false); }
6. Code for Popular Posts List
Please note that this uses the WP-PostViews plugin.
Place this code in a separate PHP Code Widget in the sidebar:
<!-- POPULAR POSTS --> <span> <h3 class="widgettitle ex-noline">Popular Posts</h3> <span class="ex-link"> <a id="sidebar-popular-link" href="javascript:ExpandCollapse(document.getElementById('sidebar-popular'), 'Popular Posts List')"> <span id="sidebar-popular-sign" class="ex-sign"></span> </a> </span> </span> <p class="ex-spacer"> </p> <div id="sidebar-popular" class="widget widget_categories"> <ul> <?php get_most_viewed('post', 5, 0, true); ?> </ul> </div>
Place this code in the theme’s footer.php file:
// POPULAR POSTS if ( !IsCookieSet('sidebar-popular') ) { SidebarWidget('sidebar-popular', true, 'Popular Posts List', true); // Initial display is collapsed } else { SidebarWidget('sidebar-popular', IsCollapsed('sidebar-popular'), 'Popular Posts List', false); }
7. Code for Recent Posts List
Place this code in a separate PHP Code Widget in the sidebar:
<!-- RECENT POSTS --> <span> <h3 class="widgettitle ex-noline">Recent Posts</h3> <span class="ex-link"> <a id="sidebar-recent-link" href="javascript:ExpandCollapse(document.getElementById('sidebar-recent'), 'Recent Posts List')"> <span id="sidebar-recent-sign" class="ex-sign"></span> </a> </span> </span> <p class="ex-spacer"> </p> <div id="sidebar-recent" class="widget widget_categories"> <ul> <?php global $post; $myposts = get_posts('numberposts=5'); foreach($myposts as $post) : setup_postdata($post); ?> <li><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></li> <?php endforeach; ?> </ul> </div>
External link: get_posts()
Place this code in the theme’s footer.php file:
// RECENT POSTS if ( !IsCookieSet('sidebar-recent') ) { SidebarWidget('sidebar-recent', true, 'Recent Posts List', true); // Initial display is collapsed } else { SidebarWidget('sidebar-recent', IsCollapsed('sidebar-recent'), 'Recent Posts List', false); }
8. Code for Tag Cloud
Please note that this uses the Configurable Tag Cloud (CTC) plugin.
Place this code in a separate PHP Code Widget in the sidebar:
<!-- TAG CLOUD --> <span> <h3 class="widgettitle ex-noline">Tags</h3> <span class="ex-link"> <a id="sidebar-tags-link" href="javascript:ExpandCollapse(document.getElementById('sidebar-tags'), 'Tag Cloud')"> <span id="sidebar-tags-sign" class="ex-sign"></span> </a> </span> </span> <p class="ex-spacer"> </p> <div id="sidebar-tags" class="widget widget_categories" > <br /> <ul> <?php ctc('smallest=12&largest=25&unit=px&mincolor=#505050&maxcolor=#c0c0c0&showcount=no'); ?> </ul> </div>
Place this code in the theme’s footer.php file:
// TAG CLOUD if ( !IsCookieSet('sidebar-tags') ) { SidebarWidget('sidebar-tags', false, 'Tag Cloud', true); // Initial display is expanded } else { SidebarWidget('sidebar-tags', IsCollapsed('sidebar-tags'), 'Tag Cloud', false); }
awesome. i’ve scan read through a few of ur posts now. some great info.
the post above is great
just wondering, is it not possible to just write a plugin and have everything just added in so that little or no work is required to get the effect?
i’ll bookmark ur website. keep up the good work 🙂
omar
Thanks omar,
…just wondering, is it not possible to just write a plugin and have everything just added in so that little or no work is required to get the effect?
Actually, I started work on a plugin a while back. It’s a bout 75% complete, but I’ve not had the time recently to finish it. Perhaps I’ll finish it over the holidays.
Regards, Steve.
Thanks Steve, I’m using the same template…found the shortcode. Excellent.
Cheers, San.
Hi San,
…I’m using the same template…found the shortcode…
Sorry. Didn’t realise you’re using the same template. Glad you got it working.
Regards, Steve.
Hi Steve,
Excellent mods…clearly explained and easy to follow for relatively newbies like me.
One question: can the expand/collapse code also be used in the blog itself…I love the way you have the expand/collapse feature used for the “Table of Contents” chapter on this page, and would like to use that on my site as well.
Cheers, San
Thanks San,
….can the expand/collapse code also be used in the blog itself…. for the “Table of Contents”….
The TOC is created using a couple of shortcodes that come with the theme I use. Very handy.
I took a look at the HTML code the shortcodes create in the hope of being able to reproduce it. While this HTML code would be fairly easy to reproduce, the jQuery that controls the display is not. In short, while it’s possible to do it’s not particularly straightforward.
Regards, Steve.
HI Steve, Your website is an exceptionally useful as far as a blog goes, specially in my case since I design website themes. Finding gaps in infocus (which I consider is a near perfect theme) is helpful so I can add them to my own designs ; )
I just wanted to thank you for having this particular site, I wish I had it found it sooner. Btw, you should check out the new functionalities of WordPress 3.0, which is incredible since it includes different post types customisable menus and a whole lot more.
Hi nadeeja,
Many thanks. It’s good to hear you’ve found the content useful.
I’ve had WordPress 3.0 with inFocus running on a local server for a while now but have yet to move it into production. I agree with you though, it offers far more than 2.x.
Regards, Steve.
Hi Steve. Thx for response! It works.
One more question: which “tool” do you use to show the code in your posts?
THX! great site by the way
Thanks deftru. Glad it worked.
For code high-lighting I use the Syntax Highlighter MT plugin which incorporates the SyntaxHighlighter JavaScript package by Alex Gorbatchev. The author has also provided a WordPress plugin. They’re both very similar. I simply prefer the former.
hi! Is it possible to use this in the category-template. do i have to adapt the code?
Hi deftru,
Do you mean category list or category template? If it’s the category list, I’ve provided the code needed. If it’s the latter, it’s independent of the template and therefore you shouldn’t need to change the code.
You’re welcome Tolu.
I would sure try this out and give you some feedback. You’re a life-saver! Thanks
I noticed that too and simply assumed that was the developer’s intent.
The file that displays single blog posts: /lib/includes/template-single.php, includes code to display images for single portfolio pages but not single blog posts. Whether this was the intent or an omission I don’t know.
However, to fix this, open /lib/includes/template-single.php and locate the code block that begins on or around line 177 with <div class="top_metadata"> and ends on or around line 205 with </div>.
Immediately below this code insert the following code block:
I’ve tested this on my local installI’m using this on my production server and it works seemingly without issue. One thing I did notice though was that when you mouse-over the image it fades out slightly. You can correct this by commenting-out this line of code on line 98:The caveat is that this is merely a hack and as I don’t use portfolio pages I don’t know if or how it will effect them.
Hope this helps.
Steve.
Note: A more comprehensive solution is detailed here.
I just got the theme working on my local installation, I noticed the post images don’t show on the individual post pages, how can I work around this? Thanks in anticipation. By the way, you’re doing a great job!
You’re welcome John. I’m glad you found it useful.
Excellent tutorial, thank you. I was searching for a plugin that would display an archives widget with expandable text for the years, I guess that’s possible using this.