Friday, July 9, 2010

Visual Navigation

There are a few sites that have a navigation menu that can highlight / change when the menu's target scrolls into view (e.g. brizk design & Crush + Lovely) . I tried to generalize the script so it would work with any site and I ended making up with this jQuery plugin.


I set up a demo page here.

Download: Uncompressed | Minified | Zipped Demo | Github

I have tested this plugin in IE8, Firefox and Chrome. Please leave me feedback if you any problems with how it works or if it doesn't work in other browsers.

Setup
  • This plugin requires jQuery in order to function properly.
  • Follow the basic templates below to set this up on your site. For more details, go to the specific section.
Default Setup
  • HTML
    <div id="sidemenu">
    <ul>
    <li><a href="#home">Home</a></li>
    <li><a href="#work">Work</a></li>
    <li><a href="#blog">Blog</a></li>
    <li><a href="#projects">Projects</a></li>
    <li><a href="#about">About</a></li>
    <li><a href="#contact">Contact</a></li>
    </ul>
    </div>
    • Change the side menu id to whatever you want, just make sure you target it with the script.
    • This side menu example will work with the plugin's default settings. See the alternate example of a different layout.
  • CSS
    #sidemenu { position: fixed; top: 50px; left: 20px; background: #444; width: 120px; }
    #sidemenu ul { list-style-type: none; margin: 0; padding: 0; }
    #sidemenu li { margin: 5px; padding: 5px; width: 100px; text-align: center; }
    #sidemenu li.selected { background: #555; }
    #sidemenu a { text-decoration: none; color: #bbbbff; }
    #sidemenu a:hover { color: #fff; }
    • This CSS highly variable. Change the position, size and colors as desired.

  • Script
    $(document).ready(function(){
    $('#sidemenu').visualNav();
    });
Alternate Setup
  • HTML
    <div id="menu">
    <div class="link" title="#Home">Home</div>
    <div class="link" title="#work">Work</div>
    <div class="link" title="#blog">Blog</div>
    <div class="link" title="#projects">Projects</div>
    <div class="link" title=".about">About</div>
    <div class="link" title=".contact">Contact</div>
    </div>
    • This side menu uses divs with a title attribute. The value in this attribute can be used to target an Id or a class (which should be unique).
    • It is important to note, that this menu will not work with javascript disabled, whereas the default one will work.
  • CSS
    #menu { position: fixed; top: 50px; left: 20px; background: #444; width: 120px; }
    #menu div.link { margin: 5px; padding: 5px; width: 100px; text-align: center; }
    #menu div.selected { background: #555; }
  • Script
    $(document).ready(function(){
    $('#menu').visualNav({
    link              : 'div.link',
    targetAttr        : 'title',
    selectedAppliedTo : 'div.link'
    });
    });
Customizing / Options
This plugin has the following default options, so you will only need to include the line below in the script options if you want to change the default:
$('#sidemenu').visualNav({
link              : 'a',        // Add a link class, as necessary
targetAttr        : 'href',     // added in case you have link = "div" and attribute something like
selectedClass     : 'selected', // css class applied to menu
selectedAppliedTo : 'li',       // to only apply to the link, use "a"
topRange          : 100,        // measure from the top of the viewport to X pixels down
topMargin         : 100,        // margin above the top where the target updates the menu
bottomMargin      : 20,         // margin from the end of the page where the last menu item is used
animationTime     : 1200        // time in milliseconds
});
  • The first four options should were hopefully made clear in the examples above.
  • To modify how the menu acts, you will need to adjust the "topRange", "topMargin" and "bottomMargin" values. I made this picture to better understand what these values do for you.
  • topRange: The "topRange" is basically the area where target needs to be inside of in order for the menu to update. For example, the target (found in the link attribute) starts under the topRange. As it moves up and crosses into the topRange. The script then updates the side menu to show that the target area is in view (or inside the view port).
  • topMargin: The top edge margin (topMargin) is the area above the view port. The top edge margin is used while the page is being scrolled up - the target is above the view port and as you scroll up, the target moves down. When the target is inside the edge margin, the menu will update and point to that target.
  • bottomMargin: The bottom edge margin is the area below the view port. It is when the page is scrolled down (the contents are moving up). When the bottom of the page is inside the bottom edge margin, the menu will update with the last targeted id. This was necessary to be include in case the last section is too short and unable to reach the top range area.
  • animationTime: The animation time is the time in milliseconds that the menu will scroll to the selected section.
Known Problems / Bugs / Suggestions
  • The menu will not select (or highlight) the item above the last item if they are both very short. For example, if your browser shows three sections while at the bottom of the page. The third to last may have shown for a brief time just before the bottom of the page reached the bottom edge margin. The menu would then skip directly to the last menu item. This is one reason why the bottom margin value is kept a low number (20 pixels by default).
  • If you click on a menu item, the page contents will automatically scroll to that section and update the browser url with that target. But if you manually scroll the page using the scroll bar or mouse, the web page url will not update with the current position. This was done on purpose, because if the script changes the location, the page will jump to that target automatically. This wouldn't look good if you are quickly scrolling through the page as it would make the movement jittery.
  • To make suggestions or report any bugs please email me at wowmotty at g mail dot com.

42 comments:

  1. Updated to version 2.0... using a new algorithm to determine if the blocks are in the view port.

    ReplyDelete
  2. Hi, great navigation tool. Just wondering how you would make it work for a horizontal scrolling page?
    Thanks in advance!

    ReplyDelete
  3. Hi loui83!

    I have a version in the works that will have both horizontal and vertical scrolling... but I won't be able to release it for another week or so.

    ReplyDelete
  4. Hi, very nice plugin!

    I am also curious about when the horizontal version is coming.
    How about one that can move both vertical and horizontal?

    I have seen some similar thing here; http://www.appelsiini.net/projects/viewport/3x2.html
    But i read somewhere that this will not work in some browsers.

    thanks anyway for a nice plugin.

    O

    ReplyDelete
  5. Hi Info! Get the latest version on Github which works with vertical, horizontal or both page layouts. :)

    ReplyDelete
  6. Thanks Mottie, i have downloaded it and will give it a try.
    Looks good :)

    ReplyDelete
  7. Hi,
    how do I add an external link to the menu?

    ReplyDelete
  8. @jo: Use one of the available methods to control the navigation.

    ReplyDelete
  9. Hi again,
    sorry but the link didn't make much sense to me! If I am using the horizontal menu.. e.g I have five internal links and one in this menu linking to an external blog. How can I get it to open in a new window?

    ReplyDelete
  10. Opps! Sorry I misread your question. Basically you can just add a class to each menu link, then use the "link" option to target that class name. Here is a demo.

    ReplyDelete
  11. Hi, tried to use this with a horizontal menu (188px height) that is fixed on the top of the screen , what I need to do is to scroll the section linked in the menu and positioned it just below the menu instead behind and In can´t do it. please help me.

    Thanks in advance

    ReplyDelete
  12. Hi @Charlie Hdez!

    Try this demo, maybe it will help guide you. Basically I added top padding to the ".content" to keep it below the menu.

    ReplyDelete
  13. Hey man, great plugin, only one problem i had, it doesn't really have a callback functionality. So i added my own, here is the updated code. please feel free to use or distribute my changes.

    http://pastebin.com/j74HR60m

    ReplyDelete
    Replies
    1. Thanks! I'll include this in the next update, whenever I have some time to work on it!... you can follow the progress by checking on the issue (enhancement really) I opened at my github repository for this plugin.

      Delete
  14. Hi Mottie,

    I'm having a really good time implementing the visual navigation plugin. Thanks for sharing it.

    Question. Can the scrolling animation speed be changed depending on the distance being the elements scrolled? Currently the 'animationTime' option works great, but some of my content is pretty long and having several different animation speeds would really spruce things up.

    My aim is to have at least 5-6 different animation speeds based on as many different distances, all included in the options of the plugin so they can be tweaked for each implementation. Something like... 'scrollTime1: - scrollTime6:', and 'scrollDistance1: - scrollDistance6:'.

    Thanks for the great work.

    ReplyDelete
    Replies
    1. Hiya!

      Sorry for not responding earlier, but to modify the timing it would require some core code modification. I'll look into adding something that calculates the distance it needs to move, then change the time accordingly. Maybe set it to time/pixels (completely guessing here, but something like 100ms/100pixels)? I opened a repository issue to remind myself to add this... now if I just had some free time =/

      Delete
  15. I'm using the "Alternate" method - using DIVS. When I manually scroll the page, the links highlight properly. But when I click them, I get an error in Firebug's console:

    TypeError: $sel.offset() is null

    Something I did wrong? I'm 95% sure I did everything correctly. Triple checked the demo.. etc.

    ReplyDelete
    Replies
    1. Hiya! Are you sure you have the "contentClass" name set correctly in the options? If you do, then I would need to see your HTML and plugin settings in order to better help you.

      Delete
    2. And since I downloaded the min, I didn't see those settings.

      Awesome... works now. Thanks.

      Delete
  16. This plugin is excellent. It's just what I needed for a site I'm working on. Thanks for your hard work. You really should provide a "donate $$ for a license" link of some kind.

    I'm having one small problem I hope you can help with. I've called your script on a pretty basic HTML layout with a fixed nav of UL/LI. The 'selected' LI is updating beautifully. And when I click one of the nav links, the page does go to the appropriate DIV. But it does not animate. It's a basic jump, working the way any traditional anchor would. I would really like the more aesthetically pleasing animation rather than this abrupt jump, but I can't get it to animate.

    I have also imported jquery easing 1.3. Also, many of my content DIVs are styled with height:100% so they fit in the whole window. Could this be part of the problem?

    Any thoughts?

    ReplyDelete
    Replies
    1. It sounds like there might be a script error or some kind of interference. Press F12 in your browser, then check the console tab. See if there are any javascript errors that might interrupt the execution of javascript.

      If you can't find anything, it would make it easier for me to help if you shared a demo - modify this demo to try and replicate the issue. Thanks!

      Delete
    2. Thanks for the reply.

      I switched to the non-mini script to get an actual line number:

      Uncaught TypeError: Cannot read property 'left' of undefined, Line 76

      I'm doing vertical scroll only, if that tells you anything.

      I don't think I can really replicate my structure on jsFiddle, but I'll email you a link.

      Thanks for any help you can offer. To reiterate: it WORKS, very nicely actually, I'm just hoping for the added bonus of a nice animated scroll when you click a nav link.

      Delete
  17. Thanks for the reply.

    I switched to the non-mini script to get an actual line number:

    Uncaught TypeError: Cannot read property 'left' of undefined, Line 76

    I'm doing vertical scroll only, if that tells you anything.

    I don't think I can really replicate my structure on jsFiddle, but I'll email you a link.

    Thanks for any help you can offer. To reiterate: it WORKS, very nicely actually, I'm just hoping for the added bonus of a nice animated scroll when you click a nav link.

    ReplyDelete
    Replies
    1. Wow, I totally forgot about these comments here. Now I'm not sure what the issue could be.

      * Are you using jQuery v1.3+?
      * Is the HTML set up with each content block properly and has a class name of "content"?
      * Does the link href match an ID or class name somewhere on the page? I know older IE has issues with mixed upper and lower cases in ID/classes.

      Delete
  18. Hi... I am a beginner and I am trying to incorporate the visualnav script into Twitter's Bootstrap framework.

    I just want a simple smooth vertical scrolling animation. When you click on something, the page scrolls beautifully to the desired option.

    Could you give any advice on this matter? Thank you.

    ReplyDelete
    Replies
    1. Hi! I'm sorry it took me so long to respond, but I'll have to add instructions for this on my to do list. =(

      Delete
    2. Ok I've finally gotten around to updating the plugin. Check out the new Bootstrap demo.

      Delete
  19. Hi, Thanks so much, this is fantastic!
    I have a question horizontal version. How can I make it automatically load to a given section. eg. it first loads to "section 3" instead of "section 1"? So when you first view the page you can scroll left or right, instead of just right.
    Thanks

    ReplyDelete
  20. Hi Rob - love your plugin and just used it as a learning exercise to get it to work in a one page WordPress theme I'm working on.

    As a complete JS/jQ novice, it's been several days of disappointment culminating in that amazing EUREKA! moment we all love so much.

    I was originally looking to combine 2 or even 3 plugins to achieve what I've been able to do with just yours - awesome!

    Thank you very much for your hard work.

    Mike :)

    ReplyDelete
  21. LOL - there's always a question isn't there...

    I'm having difficulty using a link in the menu to the internal blog page (posts page). Adding a standard link to the menu breaks the menu highlighting.

    Secondly, once I'm on a different page, even one which uses the same menu I can't get back because the new page takes control of all the URLs.

    i.e. Going from mydomain.com/#home to mydomain.com/blog means that after that, clicking menu items gives me mydomain.com/blog#home, mydomain.com/blog#about, mydomain.com/blog#contact etc.

    Did I miss something?

    Thanks :)

    ReplyDelete
    Replies
    1. External links should get a class name of "external" added to them so that the plugin ignores them - the class name can be changed by changing the "externalLinks" option.

      As for linking from different pages, you'll need to change the links in your blog to point to "mydomain.com#home" (or maybe just "../#home") instead of "#home", or maybe I'm not understanding... why would you use the same links on a different page?

      Delete
  22. Hi Rob, i absolutely love your plugin!

    But i have a problem here... i can't figure how and were should i update the topMargin value... i need my content area to be placed arround 150px bellow the top of the browser...

    Thanks in advance!

    ReplyDelete
    Replies
    1. Hi Andre!

      The topMargin option is only for measurement. The problem with using the hash tag is that the browser forces the anchor to be at the top of the page; it ignores any margin and padding as well. This plugin updates the hash when the animation completes, so if you try to offset the top, it jumps back to the top edge of the anchor.

      So, the best, cross-browser solution is to just add an anchor (link) above your content, then add a margin or padding to push the content down. Check out what I had to do for the bootstrap demo - I added a 50px top padding above the content.

      Delete
    2. :P Many thanks!!!! That do the trick!

      Again, thanks for all your work!


      Delete
  23. Hi!
    Excuse me for poor english.

    It's normal that Horizontal Navigation don't work properly on smartphones?

    ReplyDelete
    Replies
    1. Hi Valentino!

      I'm sorry I haven't had a chance to test this plugin on a smart phone. Last time I checked, the menu doesn't stay fixed because the css class "position: fixed" doesn't work.

      What problem are you having?

      Delete
  24. Hi!

    Thank you for this GREAT script! I'm planning to use it in a vertical design. (How) can I make the hash update while scrolling?

    ReplyDelete
    Replies
    1. Hi Bas Sijpkes!

      The problem is that when the hash changes, the browser automatically jumps to that element, so the page will jump while scrolling. There is one way around it and that is to rename all of the IDs on the page, this might be possible if your stylesheet doesn't use ID's for styles... either way, I don't have much time right now to implement something like that.

      Delete