How to toggle a theme region using jQuery with either Omega or AdaptiveTheme
Technical article (Planet Drupal)
Recently two clients wanted to have a left-column navigation menu which the user could hide or display using a link in the primary menu, one on a site with a theme based on Omega, and another using a theme based on AdaptiveTheme. It wasn't enough to call the jQuery function .toggle()
, however, since by the time the page has been loaded the Omega base theme has already determined that the Sidebar First region has content and the width of the rest of the page elements have been determined accordingly. Fortunately, the Omega and AdaptiveTheme base themes use CSS classes to indicate which theme regions are active, and set the width of each region based on those classes. So, all we had to do was to swap the appropriate classes each time the we toggled the display of the menu.
As it happens this is the first time I've had to insert random bits of jQuery into a theme (up until now, there was always a module for that), and I found that the necessary jQuery wrapper wasn't as well documented as it could have been, so here's a quick tutorial.
Put the following into MYTHEME.info
:
scripts[] = js/toggle.js
In the file js/toggle.js
, put the following for a theme based on Omega:
(function ($) {
Drupal.behaviors.MY_OMEGA_SUBTHEME = {
attach: function(context, settings) {
// initialize
var initialState = $.cookie('MYTHEME_toggle');
if ( initialState == "1" ) {
$('aside#region-sidebar-first').hide();
$('div#region-content').toggleClass('grid-16 grid-11');
}
// attach to menu item "Show/Hide Menu"
var showOrHideMenu = $('aside#region-sidebar-first').is(':hidden');
$('.menu-12345 a').click(function(){
if ( showOrHideMenu == true ) {
// div#region-content.grid-11
$('aside#region-sidebar-first').show();
$('div#region-content').toggleClass('grid-11 grid-16');
$.cookie('MYTHEME_toggle', 0, { path: settings.basePath });
showOrHideMenu = false;
}
else if ( showOrHideMenu == false ) {
// div#region-content.grid-16
$('aside#region-sidebar-first').hide();
$('div#region-content').toggleClass('grid-16 grid-11');
$.cookie('MYTHEME_toggle', 1, { path: settings.basePath });
showOrHideMenu = true;
}
return false;
});
}
};
})(jQuery);
On our Omega based site, the central content region div#region-content
has the class .grid-11
when the left sidebar is active, and .grid-16
otherwise (we have no right sidebar on this site). This means that when the page is loaded, the menu is displayed and the content region has class .grid-11
. Clicking the menu item with ID 12345 will hide the menu, and change that class to .grid-16
, which has the effect of making the content region wider. Clicking this menu item again will make the page return to its initial state.
You'll notice that this code snippet also sets a cookie to make sure that the menu toggle state is preserved when the user loads a new page, using a cookie called MYTHEME_toggle
which is set to 1
if the menu should be hidden and 0
otherwise. When this file is first loaded, it checks this cookie to set the initial state of the block. All of this works beautifully, but there's a small catch -- Drupal 7 ships with jquery.cookie.js
, but it's not loaded by default for all users. So, we need to make sure that happens by explicitly loading it in a module:
/**
* Implements hook_init().
*/
function MYMODULE_init() {
drupal_add_library('system', 'jquery.cookie');
}
The code for AdaptiveTheme is very similar, except that the magic CSS classes which list the active regions are on the body
tag, and in this case we had to toggle the right sidebar:
(function ($) {
Drupal.behaviors.MY_ADAPTIVETHEME_SUBTHEME = {
attach: function(context, settings) {
// initialize
var initialState = $.cookie('MYTHEME_toggle');
if ( initialState == "1" ) {
$('.region-sidebar-second').hide();
$('body.html').addClass('no-sidebars');
$('body.html').removeClass('one-sidebar');
$('body.html').removeClass('sidebar-second');
}
// attach to "Show/Hide Menu"
var showOrHideMenu = $('.region-sidebar-second').is(':hidden');
$('.menu-12345 a').click(function(){
if ( showOrHideMenu == true ) {
// body.one-sidebar.sidebar-second
$('.region-sidebar-second').show();
$('body.html').removeClass('no-sidebars');
$('body.html').addClass('one-sidebar');
$('body.html').addClass('sidebar-second');
$.cookie('MYTHEME_toggle', 0, { path: settings.basePath });
showOrHideMenu = false;
}
else if ( showOrHideMenu == false ) {
// body.no-sidebars
$('.region-sidebar-second').hide();
$('body.html').addClass('no-sidebars');
$('body.html').removeClass('one-sidebar');
$('body.html').removeClass('sidebar-second');
$.cookie('MYTHEME_toggle', 1, { path: settings.basePath });
showOrHideMenu = true;
}
return false;
});
}
};
})(jQuery);
Since Drupal 7 menu items can normally only point towards external URLs or internal Drupal paths, we used the module Void Menu to allow us to create a menu item pointing to <a href="https://www.koumbit.org/en/taxonomy/term/26#"></a>
. Normally this would make the browser jump to the top of the page in addition to calling the attached function, but this can be avoided by returning false
.
To use this on your own site, compare the HTML generated by your theme on pages with and without the region you want to toggle. (You can do this by manually setting the menu block in question to not display on one particular page to see how your theme sets the content region in order to take the full width of the page.) Next, tweak the above code snippet so that .toggleClass()
, .removeClass()
, and .addClass()
switch the correct classes, and so that the function is attached to the correct menu ID.
Happy theming, and may your blocks always be toggled!