Advanced docking using jQuery

June 1, 2009

Some time ago I was doing some proof of concept: how Visual Studio docking functionality can be done with jQuery and unordered lists. Basically, the main goal was to implement multiple docking and undocking functionality. This tutorial will show you the results of PoC.

Download the example View live demo

The requirements

This is how docking should behave:

  1. When user hovers an item on vertical menu, its submenu should slide in from left to right (points 1 and 2 in the image below) and overlay the content
  2. When user move the mouse pointer outside the panel it should slide back
  3. If user clicks on "Dock" link (point 3 in the image below), panel should fix in the current position while content moves to the right of the panel in order to be seen
  4. If users "undock" the panel it should slide back

This way users can chose whether they want to perform an action and continue with work or they want to have available actions permanently visible.

But that is not all. I wanted multiple panes to be able to dock in the same time. If one panel only is docked it should be 100% height. With each new panel docked, height should be recalculated so that all panels have the same height value (like in the image below). If there are docked panels and user wants to slide in another panel temporarily, it should overlay docked panels.

 

A "brief" explanation

The idea was to have a navigation based on nested ULs. This is nothing new, but I wanted to create more than just CSS&UL based menus that can be used as toolboxes or action links as well. So the structure is very simple. The main UL with id="dock" will act as a vertical navigation bar that contains three links (in this example). Each link will have a nested UL that will represent a docking panel with some sample list items. Each first listitem will act as a header for docking panel, containing panel title and Dock/Undock exclusive links.


<ul id="dock">
    <li id="links">
        <ul class="free">
            <li class="header">
                <a href="#" class="dock">Dock</a>
                <a href="#" class="undock">Undock</a>Links
            </li>
            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>

            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>
        </ul>
    </li>
    <li id="files">
        <ul class="free">
            <li class="header">
                <a href="#" class="dock">Dock</a>
                <a href="#" class="undock">Undock</a>Files
            </li>
            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>
            <li><a href="#">This is one item</a></li>

            <li><a href="#">This is one item</a></li>
        </ul>
    </li>
    <!-- more submenus here -->
</ul>
<div id="content">
    <!-- content here -->
</div>

Let's see how CSS can help use to style this lists.


body{margin:0px; font-family:Arial, Sans-Serif; font-size:13px;}
/* dock */
#dock{margin:0px; padding:0px; list-style:none; position:fixed; top:0px; height:100%;
z-index:100; background-color:#f0f0f0}
#dock > li {width:40px; height:120px; margin: 0 0 1px 0; background-color:#dcdcdc;
background-repeat:no-repeat; background-position:left center;}
#dock #links {background-image:url(links.png);}
#dock #files {background-image:url(files.png);}
#dock #tools {background-image:url(tools.png);}
#dock > li:hover {background-position:-40px 0px;}
#content {margin: 10px 0 0 60px;}

The main #dock UL has fixed position in the top left corner of the window in order to be visible after scrolling. Each listitem in #dock unordered list presents one vertical link which hovering causes its child list (or simply – docking panel) to slide-in. I used CSS sprites to show hover states for each LI.


body{margin:0px; font-family:Arial, Sans-Serif; font-size:13px;}
/* dock */
#dock{margin:0px; padding:0px; list-style:none; position:fixed; top:0px; height:100%;
    z-index:100; background-color:#f0f0f0; left:0px;}
#dock > li {width:40px; height:120px; margin: 0 0 1px 0; background-color:#dcdcdc;
    background-repeat:no-repeat; background-position:left center;}
#dock #links {background-image:url(links.png);}
#dock #files {background-image:url(files.png);}
#dock #tools {background-image:url(tools.png);}
#dock > li:hover {background-position:-40px 0px;}

Also, we need to style docking panels. First three lines in this CSS script define styles of listitems on docking panel in normal and hover state. Fourth line sets visibility of nested UL's while hovering its parent LI's. Next line defines the docking panel itself. Each panel is positioned absolutely, and initially will be hidden (left: -180). Also each one of them will have z-index:-1 in order to be shown above docked UL's that have z-index:-2.

So far, so good. If wanted to create a simple CSS based navigation I could have done it with CSS only. But I want the panels to slide-in and slide-out on hover.


$("#dock li ul").height($(window).height());
$("#dock li").hover(function(){
    $(this).find("ul").animate({left:"40px"}, 200);
}, function(){
    $(this).find("ul.free").animate({left:"-180px"}, 200);
});

This jQuery scripts does exactly what I want. Te first line sets the height for each panel to be the same as window height. Now we have menus that work.

Docking/undocking functionality

Now when menu works, I can add docking functionality. The goal is to be able to dock as many panels as I want, and they all should be visible one below each other. To achieve this I need to count how many panels are docked and to recalculate their heights to fit inside the window. When panel is docked, its "free" CSS class will be replaced with "docked" class in order to have a clear distinction between docked and undocked items and to ease the manipulation.

The code below then recalculates heights for each docked panel and hides "dock" link and shows "undock" link. It will also check for number of docked items in order to move content to the right if at least one panel is docked.


$(document).ready(function(){
    var docked = 0;
    
    $("#dock li ul").height($(window).height());
            
    $("#dock .dock").click(function(){
        $(this).parent().parent().addClass("docked").removeClass("free");
        
        docked += 1;
        var dockH = ($(window).height()) / docked
        var dockT = 0;               
        
        $("#dock li ul.docked").each(function(){
            $(this).height(dockH).css("top", dockT + "px");
            dockT += dockH;
        });
        $(this).parent().find(".undock").show();
        $(this).hide();
        
        if (docked > 0)
            $("#content").css("margin-left","250px");
        else
            $("#content").css("margin-left", "60px");
    });
});

Undocking functionality is very similar. It actually do an opposite actions in comparing to docking functionality :)


     $("#dock .undock").click(function(){
        $(this).parent().parent().addClass("free").removeClass("docked")
            .animate({left:"-180px"}, 200).height($(window).height()).css("top", "0px");
        
        docked = docked - 1;
        var dockH = ($(window).height()) / docked
        var dockT = 0;              
       
        $("#dock li ul.docked").each(function(){
            $(this).height(dockH).css("top", dockT + "px");
            dockT += dockH;
        });
        $(this).parent().find(".dock").show();
        $(this).hide();
       
        if (docked > 0)
            $("#content").css("margin-left", "250px");
        else
            $("#content").css("margin-left", "60px");
    }); 

It can be optimized and polished or even used to create a plugin, but that will have to wait for a while. I hope you will make some use of this.

Let's discuss this on twitter.

56 Comments

  • Gaya (June 1, 2009)

    This looks really cool, Janko! This gives a nice user experience. Might be good for CMSes! Good job dude!

  • Neil (June 1, 2009)

    Very Nice….would be perfect for an admin console

  • Simon (June 1, 2009)

    Very nice ! Good work !
    As already said, this could be perfect for an admin console.

  • Anthony Grace (June 1, 2009)

    Very nice work and extremely useful! :-)

  • Soh (June 1, 2009)

    Sweet, thanks for this tut!

  • Ben Tremblay (June 1, 2009)

    Sweet.

    Do you ever use <a href="http://www.netvibes.com/">NetVibes</a&gt;? Not saying that’s the design I’m looking to achieve, but that’s the sort of functionality I need. This here is definitely a step in that direction.

    p.s. Name and EMail in your comment form are white text / white background. The country drop-down too. (FF2)

  • Nicolas Pascual (June 1, 2009)

    Great tutorial Janko! Like always you do!

  • Kawsar Ali (June 1, 2009)

    Really cool tutorial. This could be useful.

  • Allan (June 2, 2009)

    This is good and all, but as Jacob Nielsen points out in his recent article about mega-menu’s ( http://www.useit.com/alertbox/mega-dropdown-menus.html ) adding a .5 second delay to the hover increases usability (according to their studies). Using the jQuery HoverIntent plug in makes this easy: http://cherne.net/brian/resources/jquery.hoverIntent.html

    I’ll be subscribing to your feed. Thanks for publishing!

  • Mina Saad (June 2, 2009)

    Wowwwwwwwwwwwwwww great

  • Farooq (June 2, 2009)

    really nice and good tutorial and jquery plugin

  • Janko (June 2, 2009)

    Thanks everyone!

    Ben: I don’t use NetVibes but their UI is interesting :) I checked in FF2 and it works fine. Do you use any toolbar? Some readers reported similar issues with Google toolbar.

    Allan: Good point about delay. Thanks!

  • Davide Espertini (June 2, 2009)

    Great! Nice tutorial!

  • jpablobr (June 2, 2009)

    Excellent, thank you!

  • Marko (June 2, 2009)

    Great article as always! Bookmarked ;)

  • Tom (June 2, 2009)

    Wow.

    The things people do with jQuery continues to amaze me.

  • Muhammad Mosa (June 2, 2009)

    Mate, this is a killer post. have you considered making this as plugin or widget?!
    It would be great if you do it

  • Viking Beard (June 2, 2009)

    Unbelievable tutorial…this same functionality written in .net would be hundreds of lines of code. Thank you thank you thank you thank you!

  • Janko (June 2, 2009)

    Thanks guys!!!

    Muhammad: Yep, I think plugin will come out at the end. There are some things to consider in order to make it pluggable everywhere.

  • Ruud (June 3, 2009)

    awesome post Janko, thank you!

  • Amr Elsehemy (June 3, 2009)

    Nicely written, excellent job and very nice demo.

  • Russ (June 3, 2009)

    This is a very nice bit of code. I would suggest a little addition to capture the onResize event and adjust things when the vertical / height of the page changes.

  • JQ (June 3, 2009)

    Great idea. I could see this being used for many for many different sites. It would really depend on the context of the site that’s using it. There are pros and cons to implement something like this.

    Here are a few off the top of my head.

    Pros:

    – It is an amazing idea for site with a large navigation system
    – Combined with animation it’s adds a stickiness and a unique experience
    – Users will love the ability to ‘customize’ the navigation

    Cons:

    – Usability the dock / undock states are not maintained on page refresh
    – The moving menus may disrupt the natural flow of the site
    – It might pose a problem to the organic traffic / SEO strategies

    Again it all really depends on the context of the site will determine if this functionality will be a benefit.

    Great work dude.

  • jzigbe (June 4, 2009)

    Very Nice! Thanks

  • Abdel (June 4, 2009)

    Really good tutorial.. Thanks!

  • Nathan (June 5, 2009)

    Doesn’t work on Chromium though :)

  • Janko (June 5, 2009)

    Thanks everyone for comments!

    Russ: That is one of the thing’s I’ll definitively add!

    JQ: I agree, this one really depends on the context. I believe that it can be more useful in web applications and admin sites as a toolbox, than navigation on public website.

    Nathan: It’s important to work in Chrome :D

  • Dave D (June 5, 2009)

    Thanks! detailed and useful. By looking at the title, I thought it is about a mac-like dock. If anyone is interested – there’s a cool example on coderun.com
    Really impressed by what you’ve done with jQuery.

  • Liu Hao (June 8, 2009)

    Thanks.really cool~

  • dalton (June 8, 2009)

    Cool. I would be interested to see how you would go about docking it to the right side of the screen, taking the scrollbar into account.

  • Carlo (June 8, 2009)

    A wordpress plugin???
    Can you create it?

  • Janko (June 9, 2009)

    dalton: I guess the issue will be docked panels. In that case, only the main content should be scrollable not the entire page. That’s how it’s work in Visual Studio at least.

  • Tommaso Baldovino (June 10, 2009)

    It would be good to have an accessible version of this script: side menus should be visible even with no JavaScript enabled. You’ve done a good job indeed ;)

  • Alidad (June 13, 2009)

    this is nice, I want to use that for my site but i was wondering how can i change to the right side instead of left side!

    Alidad

  • yosry (July 4, 2009)

    Very nice work i love it but not working on IE6 :(

  • the website guy (July 10, 2009)

    I like the concept… the only thing that I don’t like is the way that it instantly disappears when you hover away. I like how it slides in – it would be better if it slid out as well. – Just my opinion

  • TheWaffleSupremacy (July 14, 2009)

    It’s awesome! Very cool what you’ve done!

    But I have to say, just for amusement, look at it on a mobile browser. I know, not what it’s intended for, but it still gave me a chuckle. ;)

  • Gadget (July 15, 2009)

    Looks like I done everything like in the example. But I still have some problems..

  • Mohammad Saad (July 25, 2009)

    it does not work in IE 6… any solution??

  • swaino (July 25, 2009)

    Hi,
    Love the plugin, but I was wondering is there something written in JQuery that works like the (dare I say!) Coolite/ExtJS window docking system. I love the way you can add N,E,S,West panes and have everything dock…

  • Firebubble Design (July 28, 2009)

    Great tutorial, i could see this being very useful for future sites with large navigations, thanks for the post.

  • SJL Web Design (July 29, 2009)

    This would be perfect for an admin area of a CMS, thanks for sharing it.

  • aki (August 12, 2009)

    Awesome, my clients are very happy with this feature. Thanks!

  • Web Designer (September 18, 2009)

    Hey Janko,
    Thanks a lot for the tutorial. Very nice effect but difficult to implement as well. I would probably go for a CSS solution in this specific case.

  • Daniel Gibbs (September 21, 2009)

    Changing it from left to right? Just take a look at the code and reverse everything from left to right, it’s really straight forward (in response to a question above).

    Anyway, great post and a very logical approach. Cheers

  • Alex Similou (September 30, 2009)

    Guys,
    Do you have any idea of how I can make the script ‘remember’ the last state (docked/undocked) of the tabs? After refreshing the page the docked tabs go back to the ‘undocked’ state.

  • Richard Sharpe (October 2, 2009)

    @ Mohammad Saad

    Most things dont work in ie6, can I suggest don’t use ie6 ?

    How I wish the world was that simple a place :)

  • Matt Gregory (October 12, 2009)

    DO people really still use ie6 anymore?

  • Max (October 25, 2009)

    Awesome explanation. I’m only coming to grips with jQuery slowly but this post has helped me move a little closer to understanding the subtleties involved.

  • Michelangelo (October 26, 2009)

    Hi,

    Fantastic solution! How can I set one of these 3 panels as docked instead of UnDocked at the end of the loading of the page?

    thanks

  • diseño web (October 27, 2009)

    very good!! excellent menu! really very professional

  • Trent – Work at Home (November 9, 2009)

    I agree with the comment above in that I didn’t think anyone used IE6 anymore. Anyway, great post. I appreciate it! Thanks.

  • klaus (November 19, 2009)

    i am so happy that i found this site. iquery rocks. i still learn the demo stuff on http://docs.jquery.com/Tutorials but yours are great.

  • Ham (November 19, 2009)

    How can I make one of the items dock by default when the user loads the page? Appreciate your help.

  • Dave (January 27, 2010)

    I think a nice addition to this, if you could implement it, would be to have each of the main headings clickable to expand / contract it’s child elements similar to an accordion style menu

  • Formulis Web Design (February 15, 2010)

    Janko, that is a very cool menu system! I could see that being extremely useful in software user guides, or just about anything with complex nesting of information. Thanks!