p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/index.php)
-   BOOK: CSS Instant Results (http://p2p.wrox.com/forumdisplay.php?f=255)
-   -   Code Announcement: Ch3, Dynamic Drop Down Menus (http://p2p.wrox.com/showthread.php?t=50285)

richard.york November 14th, 2006 10:57 AM

Code Announcement: Ch3, Dynamic Drop Down Menus
 
I have prepared a drop in replacement for the drop_down_menus.js script used in the Implementing JavaScript-enabled Drop-Down Menus project in Chapter 3.

The code that I have prepared as a replacement ought to be able to survive with the same structural markup as presented in the book.

The script that appears here has a few fundamental differences with the code supplied with the book.

 * It uses a mouseover to open and close menus
 * It does not have any keyboard-based navigation

mouseover vs. click
In the book I discussed the pitfalls of a mouseover based menu system. After some further consideration I came to the conclusion that mouseover is the best way to go, but only if you also implement a "sticky" menu system. That is to say, menus that don't immediately close when the mouse leaves the menu. This script includes this "sticky" menu feature, a brief timeout is set (almost a second) so that menus don't close immediately, making this menu more accessible.

Keyboard Navigation
I'm still searching for the best way to implement keyboard-based navigation. The approach I used in the book fails when a page is large enough to scroll and the user wants to use the arrow keys to scroll the page. I believe the best approach is to use tabs for keyboard-based navigation. I will try to revisit this functionality soon.

The Code
Just replace the contents of drop_down_menus.js with the following:

Code:

/*
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\      \\\\\\\\|
//\\\ @@    @@\\\\\\| Menu Script
//\\ @@@@  @@@@\\\\\| (c) Copyright 2006 Richard York, All rights Reserved
//\\\@@@@| @@@@\\\\\|
//\\\ @@ |\\@@\\\\\\|
//\\\\  ||  \\\\\\\|
//\\\\  \\_  \\\\\\|
//\\\\\        \\\\\|
//\\\\\  ----  \@@@@|
//@@@@@\      \@@@@|
//@@@@@@\    \@@@@@|
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
*/

// addEvent functions
//
// written by Dean Edwards, 2005
// with input from Tino Zijdel
//
// http://dean.edwards.name/weblog/2005/10/add-event/

function addEvent(element, type, handler)
{
    if (typeof(element) == 'string')
    {
        element = document.getElementById(element);
    }

    if (typeof(element) == 'object' && element && handler)
    {
        // assign each event handler a unique ID
        if (!handler.$$guid)
        {
            handler.$$guid = addEvent.guid++;
        }

        // create a hash table of event types for the element
        if (!element.events)
        {
            element.events = {};
        }

        // create a hash table of event handlers for each element/event pair
        var handlers = element.events[type];

        if (!handlers)
        {
            handlers = element.events[type] = {};

            // store the existing event handler (if there is one)
            if (element['on' + type])
            {
                handlers[0] = element['on' + type];
            }
        }

        // store the event handler in the hash table
        handlers[handler.$$guid] = handler;

        // assign a global event handler to do all the work
        element['on' + type] = handleEvent;
    }
};

// a counter used to create unique IDs
addEvent.guid = 1;

function removeEvent(element, type, handler)
{
    // delete the event handler from the hash table
    if (element.events && element.events[type] && handler)
    {
        delete element.events[type][handler.$$guid];
    }
};

function handleEvent(event)
{
    var returnValue = true;

    // grab the event object (IE uses a global event object)
    event = event || fixEvent(window.event);

    // get a reference to the hash table of event handlers
    var handlers = this.events[event.type];

    // execute each event handler
    for (var i in handlers)
    {
        this.$$handleEvent = handlers[i];

        if (this.$$handleEvent(event) === false)
        {
            returnValue = false;
        }
    }

    return returnValue;
};

function fixEvent(event)
{
    // add W3C standard event methods
    event.preventDefault  = fixEvent.preventDefault;
    event.stopPropagation = fixEvent.stopPropagation;
    return event;
};

fixEvent.preventDefault = function()
{
    this.returnValue = false;
};

fixEvent.stopPropagation = function()
{
    this.cancelBubble = true;
};

var hMenu = {

    active : false,
    timer  : false,
    last  : '',

    attachEvents : function()
    {
        // Grab all the list elements with a menu class name
        // and apply a mouseover event.
        var $menus = cssQuery('ul#menu li.menu');

        for (var $i = 0, $l = $menus.length; $i < $l; $i++)
        {
            addEvent(
                $menus[$i],
                'mouseover',
                function($e)
                {
                    $e.stopPropagation();
                    hMenu.stopTimer();

                    var $childMenu = cssQuery('#' + this.id + ' > ul');

                    if ($childMenu && $childMenu.length)
                    {
                        // This bit hides sibling drop down menus, where the
                        // menu is nested more than one deep.
                        $nodes = (this.parentNode.id == 'menu')?
                            cssQuery('ul#menu ul')
                        :
                            cssQuery('li#' + this.parentNode.parentNode.id + ' ul ul');

                        if ($nodes && $nodes.length)
                        {
                            for (var $i = 0, $l = $nodes.length; $i < $l; $i++)
                            {
                                if ($nodes[$i] && $nodes[$i].style)
                                {
                                    $nodes[$i].style.visibility = 'hidden';
                                }
                            }
                        }

                        $childMenu[0].style.visibility = 'visible';
                    }
                }
            );
        }

        addEvent(
            'menu',
            'mouseover',
            function()
            {
                hMenu.active = true;
            }
        );

        addEvent(
            'menu',
            'mouseout',
            function()
            {
                hMenu.active = false;

                if (!hMenu.timer)
                {
                    hMenu.timer = setInterval('hMenu.isActive();', 800);
                }
            }
        );
    },

    isActive : function()
    {
        if (!hMenu.active)
        {
            hMenu.close();
        }
    },

    stopTimer : function()
    {
        if (hMenu.timer)
        {
            clearInterval(hMenu.timer);
            hMenu.timer = '';
        }
    },

    close : function()
    {
        var $nodes = cssQuery('ul#menu ul');

        if ($nodes.length)
        {
            for (var $i = 0, $l = $nodes.length; $i < $l; $i++)
            {
                if ($nodes[$i] && $nodes[$i].style)
                {
                    $nodes[$i].style.visibility = 'hidden';
                }
            }
        }

        clearInterval(hMenu.timer);
        hMenu.timer = '';
    }
};

addEvent(window, 'load', hMenu.attachEvents);


Regards,
Rich

--
Author,
Beginning CSS: Cascading Style Sheets For Web Design
CSS Instant Results

http://www.catb.org/~esr/faqs/smart-questions.html


All times are GMT -4. The time now is 05:46 PM.

Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.