I like Bootstrap and I like WordPress, so it’s a really good day for me when I get to combine the two. Which I’ve had the opportunity to do on a recent project we’re doing. I could go on and on about the possibilities of this awesome CSS framework and I probably will at some point, but right now I thought I’d share how I used the collapsible accordion in Bootstrap to display a particular page’s sub-pages including one level of hierarchy. A picture of the end result:
Requirements
Bootstrap’s collapsible requires its own JavaScript, which in turn requires jQuery, so make sure you enqueue both of those in your functions.php . Here’s how I added bootstrap.js :
1 2 3 4 5 |
function load_javascript_files() { wp_register_script( 'bootstrap', get_stylesheet_directory_uri() . '/bootstrap/js/bootstrap.min.js', array('jquery'), '3.0.3', true ); wp_enqueue_script( 'bootstrap' ); } add_action( 'wp_enqueue_scripts', 'load_javascript_files' ); |
Of course the bootstrap.css is also necessary.
Function for creating the Bootstrapified subpage list
The markup for the accordion differs a lot from what you get when wp_list_pages is called, so I had to do a custom function to list all the pages hierarchically. What I wanted in the end was to list children and grandchildren of the root page. This meant I had to find the oldest parent and then create the markup to list its children. I also had to find all the children of each of the previous children and create the correct markup for those. But in this case that was it, only two levels of navigation was needed.
Here’s the function I ended up with in my functions.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
function bootstrap_accordion_hierarchical_pages() { global $post; $output_markup = '<div class="panel-group" id="left-menu" role="navigation">'; $ancestors = get_post_ancestors( $post->ID ); $root = count( $ancestors ) - 1; $oldest_parent = ($ancestors) ? $ancestors[$root]: $post->ID; $mypages = get_pages( array( 'child_of' => $oldest_parent, 'hierarchical' => 0, 'parent' => $oldest_parent ) ); $counter = 1; foreach( $mypages as $page ) { $children = get_pages( array( 'child_of' => $page->ID ) ); $output_markup .= '<div class="panel panel-default">'; if ( $children ) { $output_markup .= '<div class="panel-heading has-children">' . '<a class="title-link" href="' . $page->guid . '">' . '<h4 class="panel-title">' . $page->post_title . '</h4>' . '</a>' . '<a data-toggle="collapse" class="arrow collapsed" data-parent="#left-menu"href="#collapse' . $counter . '">' . '<div class="awesome-triangle"></div>' . '</a>' . '</div>' . '<div id="collapse' . $counter . '" class="panel-collapse collapse">' . '<div class="panel-body">'; foreach( $children as $child ) { $output_markup .= '<a href="' . $child->guid . '"><h4>' . $child->post_title . '</h4></a>'; } $output_markup .= '</div></div>'; } else { $output_markup .= '<div class="panel-heading">' . '<a class="title-link" href="' . $page->guid . '">' . '<h4 class="panel-title">' . $page->post_title . '</h4> </a> </div>'; } $output_markup .= '</div>'; $counter++; } $output_markup .= '</div>'; return $output_markup; } |
I build the resulting markup step by step by first creating the surrounding elements. Next step is to fetch the correct pages, and I only wanted the first level of pages that was children to the root page, no grandchildren were wanted.
1 |
$mypages = get_pages( array( 'child_of' => $oldest_parent, 'hierarchical' => 0, 'parent' => $oldest_parent ) ); |
It’s the argument 'parent'=>$oldest_parent that determines that only direct descendants are retrieved.
In the two nestled foreach-loops the different pages’ HTML is rendered. Different markup is created depending on if there are granchildren or not. The counter is to make sure that there are unique IDs on the panels.
Displaying the result
Short and easy:
1 |
<?php echo bootstrap_accordion_hierarchical_pages() ?> |
Making it look pretty
Arguably the most important part. I wrote it in LESS syntax, but the resulting CSS can be found here. This adds all-CSS triangles to act as arrows and makes them fancy and rotatey.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
.left-side-menu-container { padding: 0; #left-menu { border-bottom: 1px solid #ece8e3; .panel { border-radius: 0; border: none; margin: 0; a { text-decoration: none; font-weight: normal; display:block; color: black; &:hover { background: #ED1A3B; color: white; } } .panel-heading { background: white; border-top: 1px solid #ece8e3; border-bottom: none; padding: 0; border-radius: 0; h4 { font-size: 14px; padding: 20px 20px; } &.has-children { a.title-link { width: 80%; display: inline-block; } a.arrow { width: 19.8%; display: inline-block; padding: 21px 22px 20px 22px; float: right; border-left: 1px solid #ece8e3; .awesome-triangle { width: 0px; height: 0px; border-style: solid; border-width: 7px 0 7px 10px; border-color: transparent transparent transparent #786860; -webkit-transition: all 0.25s ease-in-out; -ms-transition: all 0.25s ease-in-out; -o-transition: all 0.25s ease-in-out; -moz-transition: all 0.25s ease-in-out; transition: all 0.25s ease-in-out; } &:not(.collapsed) { .awesome-triangle { -webkit-transform: rotate(90deg) translate3d( 0, 0, 0); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); -moz-transform: rotate(90deg); transform: rotate(90deg); } } &:hover { .awesome-triangle { border-color: transparent transparent transparent white; } } } } } .panel-body { padding: 0; border: none; h4 { padding: 10px 20px; font-size: 14px; margin: 0; } } } } } |
And that’s it. Hope it’s of use for all y’all internetz people. Give a shout if something is odd, to praise me or correct me, or if you have questions =)
Want to create a navigation menu using Bootstrap’s navigation? I recommend the wp-bootstrap-navwalker, it’s worked out great for us!
Hey thank you so much for this. Can you explain the part where you said ”Of course the bootstrap.css is also necessary.” Because i can’t get my collapse effect to work. Probably because i don’t quite understand what you meant there.
Thanks once again
What I meant was that you need to have Bootstrap’s CSS file referenced on you page. Either as part of your own css, or reference it some other way. Are you doing that?
Thank you so much, that’s exactly what I have searched for. Good starting point for some customizing.
I first thought I could only archieve this with my custom page walker class but your solution is much easier to handle.
Hi Stina,
Thanks in advance for this neat little script.
What would be the best way to change it so that categories are used instead of pages?
kind regards,
Michael