开发者

Simple Horizontal Javascript Navigation with Prototype

I'm trying to implement a simple horizontal navigation menu that just shows a single div for each link. It is kinda like a dropdown menu but instead of a mouseover triggering a dropdown, an onclick event will trigger the showing of a div. I want to make sure I am taking the right approach before going too much further, any help is appreciated. This is what I have so far:

    <ul id="settings_nav">
        <li>
          <a>Theme</a>
          <div id="settings_block"><%= render :partial => 'email_password' %></div>
        </li>              
        <li>
          <a href="index.htm">Lists</a>
          <div id="settings_block"><%= render :partial => 'lists' %></div>
        </li>
    </ul>

      window.onload = function(){
        settingsMenuInit('settings_nav')
      }

function settingsMenuInit(settings_nav){
  $(settings_nav).childElements().each(
    function(node){
      node.onclick= function(){ this.next.show() };    
  })  
}

Something like that, but I am unsure how to get the div that is currently shown and hide it. I could iterate through all the childElements and hide each div and then show the one that is being clicked, 开发者_运维技巧but maybe there's a better way?


Some notes FW(T)W:

  1. With Prototype and similar libraries, you don't want to hook up event handlers by assigning functions to the element's onclick and similar properties; that style has several disadvantages (not least that there can only be one handler for the event on the element). Instead, use Prototype's observe function:

    someElement.observe('click', functionRefHere);
    // or
    Element.observe(someElementOrID, 'click', functionRefHere);
    

    This also lets Prototype work around some IE memory loss bugs for you.

  2. You might look at is Prototype's dom:loaded event, which happens sooner than window.onload (which won't happen until all of your images and other external resources have loaded, which can be a second or two after the page is displayed):

    document.observe('dom:loaded', initFunctionRefHere);
    
  3. You can use event delegation and just watch your settings_nav element, rather than each child node individually.

    $(settings_nav).observe('click', handleNavClick);
    function handleNavClick(event) {
        var elm = event.findElement("some CSS selector here");
        if (elm) {
            event.stop();
            // Handle it
        }
    }
    

    As you can see, Event#findElement accepts a CSS selector. It starts with the actual element that was clicked and tries to match that with the selector; if it matches, it returns the element, otherwise it goes to the parent to see if it matches; etc. So with your HTML you might look for a li (event.findElement('li')) or the link (event.findElement('a')).

    But if you want to watch each one individually, they can share a function (as they do in your example):

    $(settings_nav).childElements().invoke('observe', 'click', handleNavClick);
    function handleNavClick(event) {
        // Prototype makes `this` reference the element being observed, so
        // `this` will be the `li` element in this example.
    }
    

    Whether you watch each element individually or use event delegation depends on what you're doing (and personal preference). Whenever anything is likely to change (adding and removing navigation li elements, for instance) or when there are lots of things to watch, look to event delegation -- it's much easier simpler to deal with changing sets of elements using event delegation and just watching the parent. When dealing with a stable structure of just a few things (as in your example), it may be simpler to just watch the elements individually.

  4. Once inside your handler, you can use Element#down to find child elements (so from the li, you might use li.down('div') to find the div), or Element#next to get to the next sibling element (e.g., going from the link to the div). Either way, once you have a reference to the div, you can use Element#show and Element#hide (or Element#toggle).

  5. I recommend using named functions instead of anonymous ones (see my example above). Named functions help your tools (debuggers, browsers showing errors, etc.) help you. Just be sure not to declare a named function and use it as an expression (e.g., don't immediately assign it to something):

    // Don't do this because of browser implementation bugs:
    someElement.observe('click', function elementClickHandler(event) {
        // ...
    });
    
    // Do this instead:
    someElement.observe('click', elementClickHandler);
    function elementClickHandler(event) {
        // ...
    }
    

    ...because although you should be able to do that according to the spec, in reality various bugs in various browsers make it not work reliably (article).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜