Slide Panel Navigation

The Problem

Navigation and information architecture are hard. Even for simple sites it is easy to mess this up. In an attempt to mess it up even more I set up some criteria for a navigation that I wanted to try. Here's the criteria:

The Solution

Put the files slidenav.js and slidenav.css in some directory, I used /projects/slidenav/. In your webpage include 2 scripts in the header, the first must be to jQuery 1.4.2, and the second should be to slidenav.js. That will add the slide navigation and css to your page. It will also add event triggers and some other goodies.

The Javascript

slidenav.js


  $(document).ready(function(){
    // Add html and css to page.
    $("body").append(slidenavHtml());
    $("head").append('<link rel="stylesheet" href="/projects/slidenav/slidenav.css" type="text/css" />');
  
    // Set slidenav text to page title.
    $("#slidenav h1").html( $(document).attr("title") );
  
    // Slide up menu a few seconds after page loads.
    setTimeout("moveSlidenavUp()", 1000);
    
    // Show and hide menu if menu handle is moused over.
    $("#slidenav .handle").toggle(
      function(){ moveSlidenavDown() },
      function(){ moveSlidenavUp() }
    );
  
    // Hide the menu if they click outside the slidenav.
    $("#slidenavMask").click(function(){ moveSlidenavUp() });
  });
  
  function moveSlidenavUp(){
    var handleOffset = $("#slidenav .handle").offset().top;
    if(handleOffset > 0){
      $("#slidenav").animate({"marginTop": "-=" + handleOffset + "px"}, 200);
    }
    // Turn off the mask to fade in content.  
    $("#slidenavMask").fadeTo(200, 0.0);
    $("#slidenavMask").hide(200);
  }
  
  function moveSlidenavDown(){
    var sliderOffset = $("#slidenav").offset().top;
    if(sliderOffset <= 0){
      $("#slidenav").animate({"marginTop": "+=" + -1 * sliderOffset + "px"}, 200);
    }
    // Turn on the mask to fade out content. 
    $("#slidenavMask").show();
    $("#slidenavMask").fadeTo(200, 0.75);
  }
  
  function slidenavHtml(){
    /* Using \ allows you to create a multiline string. */
    var html = ' \
      <div id="slidenav"> \
        <ul class="menu"> \
          <li><a href="http://lesliesteward.com/">Blog</a></li> \
          <li><a href="http://lesliesteward.com/resume/">Resume</a></li> \
          <li><a href="http://lesliesteward.com/projects/">Projects</a></li> \
          <li><a href="http://lesliesteward.com/pictures/">Pictures</a></li> \
          <li><a href="http://lesliesteward.com/calendar/">Calendar</a></li> \
        </ul> \
        <div class="handle"> \
          <img src="/images/smallcoffeeman.gif" height="20px" /> \
          <h1>Leslie Steward</h1> \
        </div> \
      </div> \
      <div id="slidenavMask"></div>';
    return html;
  }
  

The Header

This is included in the header of every page you want the slidenav on.


  <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
  <script type="text/javascript" src="/projects/slidenav/slidenav.js"></script>
  

The CSS

slidenav.css


  body {
    margin: 20px auto 0 auto;
    max-width: 960px;
    padding: 1em;
  }
  
  #slidenav {
    text-align: center;
    position: absolute;
    z-index: 255;
    top: 0;
    left: 25%;
    margin: 0 auto;
    min-width: 430px;
    background-color: #fff;
    border: 1px solid #ccc;
    border-top: none;
    -moz-box-shadow: 3px 3px 5px #ccc;
    -webkit-box-shadow: 3px 3px 5px #ccc;
    box-shadow: 3px 3px 5px #ccc;
    opacity: 1;
  }
  
  #slidenav ul.menu {
    list-style-type: none;
    margin: 0;
    padding: 0;
    display: block;
    clear: both;
    height: 100px;
  }
  
  #slidenav ul.menu li {
    margin: 0;
    display: block;
    float: left;
  }
  
  #slidenav ul.menu li a {
    display: block;
    padding: 10px 15px;
    text-decoration: none;
    color: black;
    font-family: Arial, Verdana, sans-serif;
  }
  
  #slidenav ul.menu li a:hover {
    text-decoration: underline;
  }
  
  #slidenav div.handle {
    cursor: pointer;
    clear: both;
    padding: 3px 3px 0 3px;
    border-top: 1px solid #ccc;
  }
  
  #slidenav div.handle:hover {
    background-color: #cdf;
  }
  
  #slidenav h1 {
    font-family: Georgia, serif;
    font-variant: small-caps;
    font-size: 1.2em;
    margin: 0;
    padding: 0;
    display: inline;
  }
  
  #slidenavMask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 10; /* should be less than slider, but more than content */
    opacity: 0.0;
    background-color: #fff;
    display: none;
  }

The Example

Look at the top of the page. It should be there.

The Disclaimer

This code only works on my personal computer during a waxing gibbous moon. It is untested, unreview, and unlikely to work for anyone else. By loading ths page you waive the right to hold me responsible for any damage caused by it.

Notes

Starting out I wanted to have only one javascript include, but only way I could figure out is to include other files within an 'init' file, but that increased the page load, and just looked ugly. Therefore, you have to just include the jquery and the sliding navigation.

The fading content is from a mask that changes opacity. This was an afterthought, but I like it. Unfortunately, it only works for the current window view, and if you have a long page, you can see underneath it. I haven't bothered trying to figure this out.

I was really hoping to have the menu slide up and down when you hovered over the handle. But if you rapidly hovered, it would queue up the 'move up' and 'move down' events and would sometimes move completely off the screen. I tried a bunch of different techniques to solve this, but didn't find one that worked well. If you have one or want to teach me concurrency, please let me know.

Centering the slidenav is a total hack and you should find a better way.

You can break the functionality if you click the menu open, then click on the faded background content, then try to click the menu open again. I think this has something to do with combination of jQuery's toggle and my mask click function. There's probably an if statement somewhere down there that isn't working quite right, but I don't want to fix it now.