Search code examples
javascriptmenudrop-down-menutoggleprototypejs

Hiding a dropdown menu without it flashing with prototype


I have a number of dropdowns and divs that are hidden when the page loads and can be toggled with a click or mouseover, but some of them flash b/c the JavaScript does not run in time. I have their display initially set to block and then I use JavaScript/prototype to find the element and hide it. I have tried loading these "hider" functions using dom:loaded but there is still flashing. This is an example of a dropdown prototype initialization function. From http://www.makesites.cc/programming/by-makis/simple-drop-down-menu-with-prototype/:

var DropDownMenu = Class.create();
  DropDownMenu.prototype = {
     initialize: function(menuElement) {
    menuElement.childElements().each(function(node){
      // if there is a submenu
      var submenu = $A(node.getElementsByTagName("ul")).first();
      if(submenu != null){
        // make sub-menu invisible
        Element.extend(submenu).setStyle({display: 'none'});
        // toggle the visibility of the submenu
        node.onmouseover = node.onmouseout = function(){
          Element.toggle(submenu);
        }
      }
    });
  }
};

Is there a better way to hide div's or dropdowns to avoid this flashing?


Solution

  • You always run the risk of flicker when you try to hide elements after page load. I suggest you give the elements in question an inline style like display:none; or a css class with the same setting.

    From the class creation syntax used, I take it that you are using something like Prototype version 1.5.x. Here's my take on how I'd do it with that version (it would be nicer to step up to the latest version, of course):

        <script type="text/javascript">
    var DropDownMenu = Class.create();
    DropDownMenu.prototype = {
      initialize: function(menuElement) {
        // Instead of using 2 listeners for every menu, listen for
        // mouse-ing on the menu-container itself.
        // Then, find the actual menu to toggle when handling the event.
        $(menuElement).observe('mouseout', DropDownMenu.menuToggle);
        $(menuElement).observe('mouseover', DropDownMenu.menuToggle);
      }
    };
    DropDownMenu.menuToggle = function (event) {
      var menu = DropDownMenu._findMenuElement(event);
      if (menu) {Element.toggle(menu);}
    };
    DropDownMenu._findMenuElement = function (event) {
      var element = Event.element(event);
      return Element.down(element, 'ul');
    }
    var toggler = new DropDownMenu('menus');
    </script>
    

    And here is some markup to test it with (it may not match yours perfectly, but I think it is similar enough):

    <html>
    <head>
    <title>menu stuff</title>
    <style type="text/css ">
      /* minimal css */
      #menus ul, .menu-type {float: left;width: 10em;}
    </style>
    </head>
    
    <body>
    <h1>Menus</h1>
    <div id="menus">
      <div class="menu-type">
        Numeric
        <ul style="display: none;">
          <li>1</li><li>2</li><li>3</li><li>4</li>
        </ul>
      </div>
      <div class="menu-type">
        Alpha
        <ul style="display: none;">
          <li>a</li><li>b</li><li>c</li><li>d</li>
        </ul>
      </div>
      <div class="menu-type">
        Roman
        <ul style="display: none;">
          <li>I</li><li>II</li><li>III</li><li>IV</li>
        </ul>
      </div>
    </div>
    </body>
    </html>
    

    Yoda voice: "Include the prototype.js, I forgot."

    Should you want to get rid of inline styling (like I do), give the uls a class like

    .hidden {display:none;}
    

    instead, and make the DropDownMenu.menuToggle function do this

    if (menu) {Element.toggleClassName(menu, 'hidden');}
    

    instead of toggling the display property directly.

    Hope this helps.