Advanced docking using jQuery

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.

56 Responses

  1. Gaya 1. June 2009 at 15:56

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

  2. Neil 1. June 2009 at 16:37

    Very Nice….would be perfect for an admin console

  3. Simon 1. June 2009 at 17:13

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

  4. Anthony Grace 1. June 2009 at 18:36

    Very nice work and extremely useful! :-)

  5. Soh 1. June 2009 at 18:50

    Sweet, thanks for this tut!

  6. Ben Tremblay 1. June 2009 at 19:18

    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)

  7. Nicolas Pascual 1. June 2009 at 21:17

    Great tutorial Janko! Like always you do!

  8. Kawsar Ali 1. June 2009 at 21:38

    Really cool tutorial. This could be useful.

  9. Allan 2. June 2009 at 03:25

    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!

  10. Mina Saad 2. June 2009 at 07:20

    Wowwwwwwwwwwwwwww great

  11. Farooq 2. June 2009 at 07:45

    really nice and good tutorial and jquery plugin

  12. Janko 2. June 2009 at 09:33

    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!

  13. Davide Espertini 2. June 2009 at 11:55

    Great! Nice tutorial!

  14. jpablobr 2. June 2009 at 12:11

    Excellent, thank you!

  15. Marko 2. June 2009 at 13:13

    Great article as always! Bookmarked ;)

  16. Tom 2. June 2009 at 14:47

    Wow.

    The things people do with jQuery continues to amaze me.

  17. Muhammad Mosa 2. June 2009 at 16:03

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

  18. Viking Beard 2. June 2009 at 16:34

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

  19. Janko 2. June 2009 at 16:59

    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.

  20. Ruud 3. June 2009 at 09:38

    awesome post Janko, thank you!

  21. Amr Elsehemy 3. June 2009 at 13:54

    Nicely written, excellent job and very nice demo.

  22. Russ 3. June 2009 at 17:01

    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.

  23. JQ 3. June 2009 at 22:13

    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.

  24. jzigbe 4. June 2009 at 04:29

    Very Nice! Thanks

  25. Abdel 4. June 2009 at 08:28

    Really good tutorial.. Thanks!

  26. Nathan 5. June 2009 at 12:17

    Doesn’t work on Chromium though :)

  27. Janko 5. June 2009 at 12:28

    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

  28. Dave D 5. June 2009 at 15:06

    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.

  29. Liu Hao 8. June 2009 at 04:23

    Thanks.really cool~

  30. dalton 8. June 2009 at 13:37

    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.

  31. Carlo 8. June 2009 at 14:48

    A wordpress plugin???
    Can you create it?

  32. Janko 9. June 2009 at 11:25

    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.

  33. Tommaso Baldovino 10. June 2009 at 11:30

    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 ;)

  34. Alidad 13. June 2009 at 04:13

    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

  35. yosry 4. July 2009 at 17:18

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

  36. the website guy 10. July 2009 at 05:04

    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

  37. TheWaffleSupremacy 14. July 2009 at 21:21

    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. ;)

  38. Gadget 15. July 2009 at 14:53

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

  39. Mohammad Saad 25. July 2009 at 05:39

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

  40. swaino 25. July 2009 at 20:18

    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…

  41. Firebubble Design 28. July 2009 at 11:39

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

  42. SJL Web Design 29. July 2009 at 11:04

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

  43. aki 12. August 2009 at 20:50

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

  44. Web Designer 18. September 2009 at 12:05

    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.

  45. Daniel Gibbs 21. September 2009 at 10:40

    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

  46. Alex Similou 30. September 2009 at 13:09

    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.

  47. Richard Sharpe 2. October 2009 at 15:19

    @ 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 :)

  48. Matt Gregory 12. October 2009 at 17:35

    DO people really still use ie6 anymore?

  49. Max 25. October 2009 at 00:14

    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.

  50. Michelangelo 26. October 2009 at 19:57

    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

  51. diseño web 27. October 2009 at 02:02

    very good!! excellent menu! really very professional

  52. Trent - Work at Home 9. November 2009 at 03:40

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

  53. klaus 19. November 2009 at 11:54

    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.

  54. Ham 19. November 2009 at 20:55

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

  55. Dave 27. January 2010 at 15:18

    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

  56. Formulis Web Design 15. February 2010 at 21:50

    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!