Monday 8 December 2014

Dynamically activate navigation items in Bootstrap using my jQuery plugin

My challenge was to write a piece of jQuery which:

  • Takes note of the page path.
  • Compares it with the Bootstrap navigation.
  • Makes the navigation element 'active' to remind the users where they are in relation to the site.

First the HTML. Notice, that I have not set any navigation item as active. The plugin will do this. The plugin call is in red.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Activate Navigation Entry</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-default" role="navigation">
      <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Brand</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><a href="index.php">Home</a></li>
            <li><a href="about.php">About</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Dropdown <span class="caret"></span></a>
              <ul class="dropdown-menu" role="menu">
                <li><a href="action.php">Action</a></li>
                <li><a href="anotheraction.php">Another action</a></li>              
              </ul>
            </li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container -->
    </nav>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <h1>Home</h1>
        </div>
      </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
    <script src="activatenav.plugin.js"></script>
    <script>
    (function()
    {
      $(document).activatenav(
      {
        toplevel:'ul.nav.navbar-nav > li > a',
        dropdown:'ul.nav.navbar-nav > li.dropdown > ul.dropdown-menu > li > a'
      });
    })();
    </script>
  </body>
</html>
Next the jQuery plugin named 'activatenav.plugin.js'.
First calculate the end of the path. If there is no name, it must be the home page, so set that as active.
I also check if the current page is from the drop down menu. If so, set both the drop down heading and the corresponding navigation item as 'active'.
(function($)
{
    $.fn.extend(
    {
        activatenav:function(options)
        {
        var pname = window.location.pathname;
        var pArr = pname.split('/');        
            var defaults =
            {
                toplevel:'',
        dropdown:''
            };
            options = $.extend(defaults, options);
if(options.toplevel)
{
handled = $(this).handleTopLevel(options.toplevel, pArr[pArr.length-1]);
if(handled == false)
{
$(this).handleDropdown(options.dropdown, pArr[pArr.length-1]);
}
}
        }
    });

    $.fn.handleTopLevel = function(str, pth)
    {
    var hd = false;  
    if(!pth)
    {
    $(str).first().parent().addClass('active');
    }
    else
    {
    $(str).each(function()
    {
    if($(this).attr('href') == pth)
    {
    $(this).parent().addClass('active');
    hd = true;
    }    
    });
    }
    return hd;  
    };

    $.fn.handleDropdown = function(str, pth)
    {
        $(str).each(function()
    {
    if($(this).attr('href') == pth)
    {
    $(this).parent().addClass('active');
    $(this).closest('li.dropdown').addClass('active');
    }    
    });
    };
})(jQuery);