1. Menu buttons in 20 lines of jQuery

    This is my approach to menus using jQuery. I’ll go through it line by line to serve as a mini introduction to events in jQuery.

    Here’s a preview of what we’re making. The desired behavior:

    • Clicking on a button opens its menu
    • Clicking anywhere except inside the menu closes it

    I use two elements per menu: a button and a div. Clicking the button makes the div appear. The button’s name attribute marks the id of the div to open:

    <button name="the-menu" type="button" class="menu">...</button>
    <div id="the-menu" class="menu">...</div>
    

    Now the JavaScript to make it work. Each line is explained after the code:

    jQuery(function($) {
        $('button.menu').one('click', openMenu);
        function openMenu(e) {
            var button = $(this).addClass('active');
            var menu = $('#' + button.attr('name'));
            var offset = button.offset();
            var h = (button.outerHeight) ? button.outerHeight() : button.height();
            menu.addClass('active').css({
                'top': offset.top + h, 'left': offset.left
            }).click(function(e) { e.stopPropagation(); }).show(200, function() {
                $(document).one('click', {button: button, menu: menu}, closeMenu);
            });
        }
        function closeMenu(e) {
            e.data.menu.removeClass('active').hide(100, function() {
                e.data.button.removeClass('active');
            });
            e.data.button.one('click', openMenu);
        }
    });
    

    Here we go:

    jQuery(function($) {
    

    jQuery is an alias for $(document).ready. The callback is passed the jQuery object as an argument, so you can name it whatever you want. This is good practice for getting along with other JavaScript libraries. Name it $ here and it’ll work in the callback even if another library uses $.

        $('button.menu').one('click', openMenu);
    

    Attach the click event handler openMenu to all buttons with class “menu”. one is used to only respond to this event for one click. Why? Because the next click should close the menu, not open it. Why not use toggle? Because the menu should close when clicking on the document too, not just on the button, and this could put the toggle status out of sync.

        function openMenu(e) {
            var button = $(this).addClass('active');
    

    Define the openMenu callback, get the clicked button, and add a class for styling purposes.

            var menu = $('#' + button.attr('name'));
    

    Retrieve the div with the same id as the button’s name.

            var offset = button.offset();
            var h = (button.outerHeight) ? button.outerHeight() : button.height();
    

    Get the button’s position and size, because the menu will appear under it. outerHeight is used if available, provided by the Dimensions plugin, to account for borders and such.

            menu.addClass('active').css({
                'top': offset.top + h, 'left': offset.left
    

    Add a class to the menu for styling purposes, then align it to the left and bottom of the button.

            }).click(function(e) { e.stopPropagation(); }).show(200, function() {
                $(document).one('click', {button: button, menu: menu}, closeMenu);
            });
    

    Set a click handler on the menu that stops the click event from bubbling up to the document. Why? Because after the menu appears using the show method, we attach a click event to the whole document that closes the menu. If the event were allowed to propagate, clicking within the menu would close it, something you don’t necessarily always want. Notice that the call to one passes an object along with the event handler. This will be available as e.data in closeMenu.

        }
        function closeMenu(e) {
            e.data.menu.removeClass('active').hide(100, function() {
                e.data.button.removeClass('active');
            });
    

    Now define the closeMenu callback. It removes the “active” class from the menu, hides it, and also removes the “active” class from the button.

            e.data.button.one('click', openMenu);
        }
    });
    

    Finally, attach the openMenu callback to the button’s click event handler again. Remember, one is only good for one occurrence of the event.

    That’s it! Here’s the final result again. See menu.css for the necessary bits of CSS and how I made everything pretty.

Notes

  1. teethgrinder reblogged this from exogen
  2. exogen posted this