views:

113

answers:

2

demo for reference: http://greg-j.com/static-content/accordian/

When viewed in any other browser, the toggler works as you would expect it to. However, in IE (I'm using 8), you can only contract the currently expanded sub menu, and then you get undesired results once it is closed.

Example html:

<!DOCTYPE html>
<html>
<head>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"&gt;&lt;/script&gt;
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled</title>
<link type="text/css" rel="stylesheet" href="http://meyerweb.com/eric/tools/css/reset/reset.css" />
<style type="text/css">
body{margin: 50px; font-family: arial; font-size: 12px}
div{width: 172px; margin: 0 auto;}
a{text-decoration: none; color: #006ecf}
.gMenu{
    background: #fafbfc;
    border-left: solid 1px #abc1d6;
    border-right: solid 1px #abc1d6;
    padding: 5px 0;
}
.gMenu ul{
    background: white;
    border-top: solid 1px #dbe6f0;
    border-bottom: solid 1px #dbe6f0;
    position: relative;
    top: 4px;
}
.gMenu li{
    line-height: 16px;
    padding: 4px 0;
    position: relative;
}
.gMenu li li{
    padding-left: 10px;
    font-size: 11px;
}

.gMenu a{
    padding: 0 4px;
}
.gMenu ul a{
    padding: 0;
}
.gMenu a:hover{
    color: #000;
    text-decoration: underline;
}
.gMenu em{
    color: #abc1d6;
}
</style>
</head>
<body>
<div>
<ul class="gMenu">
    <li class="gm-active"><a href="#">Bodywork</a>
        <ul>
            <li><a href="#">Fairing Bolt Kits</a> <em>(23)</em></li>
            <li><a href="#">Fairing Brackets</a> <em>(2)</em></li>
            <li><a href="#">Fairings</a> <em>(17)</em></li>
            <li><a href="#">Fender Eliminator Kits</a> <em>(6)</em></li>
            <li><a href="#">Front Fenders</a> <em>(5)</em></li>
            <li><a href="#">Graphics Kits</a> <em>(223)</em></li>
            <li><a href="#">Huggers</a> <em>(40)</em></li>
            <li><a href="#">Under Trays</a> <em>(7)</em></li>
            <li><a href="#">Windscreens</a> <em>(3)</em></li>
        </ul>
    </li>

    <li><a href="#">Books</a>
        <ul>
            <li><a href="#">Service Manuals</a></li>
        </ul>
    </li>

    <li><a href="#">Brakes</a>
        <ul>
            <li><a href="#">Brake Hardware</a></li>
            <li><a href="#">Brake Lines</a></li>
            <li><a href="#">Brake Pads</a></li>
            <li><a href="#">Brake Rotors</a></li>
        </ul>
    </li>

    <li><a href="#">Controls</a>
        <ul>
            <li><a href="#">Bar Ends</a></li>
            <li><a href="#">Cables</a></li>
            <li><a href="#">Handlebars</a></li>
            <li><a href="#">Levers</a></li>
            <li><a href="#">Mirrors</a></li>
            <li><a href="#">Pedals</a></li>
            <li><a href="#">Quick Shift Kits</a></li>
            <li><a href="#">Rearsets</a></li>
            <li><a href="#">Stabilizers</a></li>
            <li><a href="#">Throttles</a></li>
            <li><a href="#">Triple Clamps</a></li>
        </ul>
    </li>

    <li><a href="#">Caps, Covers, Guards</a>
        <ul>
            <li><a href="#">Axle Covers</a></li>
            <li><a href="#">Chain Guards</a></li>
            <li><a href="#">Heel Guards</a></li>
            <li><a href="#">Mirror &amp; Signal Block Offs</a></li>
            <li><a href="#">Oil Caps and Dipsticks</a></li>
            <li><a href="#">Reservoir Covers</a></li>
        </ul>
    </li>

    <li><a href="#">Drive</a>
        <ul>
            <li><a href="#">Chain and Sprocket Kits</a></li>
            <li><a href="#">Front Countershaft Sprockets</a></li>
            <li><a href="#">O-Ring Chains</a></li>
            <li><a href="#">Rear Drive Sprockets</a></li>
            <li><a href="#">Roller Chains</a></li>
        </ul>
    </li>

    <li><a href="#">Electrical</a>
        <ul>
            <li><a href="#">Accessory Lighting</a></li>
            <li><a href="#">Alarms</a></li>
            <li><a href="#">Batteries</a></li>
            <li><a href="#">Gauges</a></li>
            <li><a href="#">Marker Lights</a></li>
            <li><a href="#">Radar Detectors</a></li>
            <li><a href="#">Spark Plugs</a></li>
            <li><a href="#">Taillights</a></li>
            <li><a href="#">Turn Signals</a></li>
        </ul>
    </li>

    <li><a href="#">Engine</a>
        <ul>
            <li><a href="#">Clutch Discs</a></li>
            <li><a href="#">Clutch Kits</a></li>
            <li><a href="#">Clutch Springs</a></li>
            <li><a href="#">Oil Filters</a></li>
        </ul>
    </li>

    <li><a href="#">Exhaust</a>
        <ul>
            <li><a href="#">Complete Exhaust Systems</a></li>
            <li><a href="#">Heat Shields</a></li>
            <li><a href="#">Slip-On Mufflers</a></li>
        </ul>
    </li>

    <li><a href="#">Frame &amp; Swingarm</a>
        <ul>
            <li><a href="#">Frame Sliders</a></li>
            <li><a href="#">Kickstands</a></li>
            <li><a href="#">License Plate Kits</a></li>
            <li><a href="#">Stand Adapters</a></li>
            <li><a href="#">Swingarm Extensions</a></li>
            <li><a href="#">Swingarm Spools</a></li>
        </ul>
    </li>

    <li><a href="#">Fuel Systems</a>
        <ul>
            <li><a href="#">Fuel Injection Mapping</a></li>
        </ul>
    </li>

    <li><a href="#">Suspension</a>
        <ul>
            <li><a href="#">Fork Seals</a></li>
            <li><a href="#">Rear Lowering Kits</a></li>
            <li><a href="#">Shock Absorbers</a></li>
        </ul>
    </li>

    <li><a href="#">Tanks</a>
        <ul>
            <li><a href="#">Gas Caps</a></li>
            <li><a href="#">Tank Bras</a></li>
            <li><a href="#">Tank Protectors</a></li>
            <li><a href="#">Traction Pads</a></li>
        </ul>
    </li>

    <li><a href="#">Intake</a>
        <ul>
            <li><a href="#">Air Filters</a></li>
            <li><a href="#">Velocity Stacks</a></li>
        </ul>
    </li>

    <li><a href="#">Tires</a>
        <ul>
            <li><a href="#">Front Tires</a></li>
            <li><a href="#">Rear Tires</a></li>
        </ul>
    </li>                
</ul>
</div>
</body>
</html>

Example JS:

(function($){

    $.fn.gMenu = function(options) {

        var o = $.extend({
            speed: "fast"
        }, options);

        return this.each(function() {

            var $ul = $('ul', this),             // Get this ul's decendant ul's
                $li = $(this).children('li');    // Get only the first decendant li's in this ul

            $ul.not('.gm-active ul').hide();

            // Create toggler elements for the first decendant li's only
            $li.children('a').after(
              $('<span>', { html: "toggle", 'class': "gm-toggler"}).hide()
            );

            var $toggler = $li.find('.gm-toggler');

            // Only show the toggler when a collapsed menu item is hovered
            $li.hover(function(){
               $('.gm-toggler', this).toggle();
            });

            // Only show one child menu at a time. Never collapse them all
            $toggler.click(function(){

                // get this togglers parent ul
                var $current = $(this).parent().find('ul');

                // slide all other child menu up and remove its .active class
                $($ul).not($current).slideUp(o.speed)
                    .parent().removeClass('gm-active');

                // Stop sliding this togglers menu if it is animating and slide it the other direction
                $current.stop(false,true).slideToggle(o.speed);

                // If this togglers menu is active ...
                $(this).parent('li').toggleClass('gm-active');
            });
        });
    };
})(jQuery);

$(document).ready(function(){
    $('.gMenu').gMenu({
        speed: 200
    });
});
A: 

Maybe this can point you in the right direction.

You don't need a return this.each(function() {}); since you are already passing the DOM element in the plug-in aka "gMenu" you just work from there on.

For brevity I left out a lot of code and minimized it to the features that matter.

Also you don't want to hide other opened menu items, since it breaks common patterns in UI design.

(function($){

    $.fn.gMenu = function(options) {

        var o = $.extend({
            speed: "fast"
        }, options);

        $('ul', this).not('.gm-active ul').hide();

        $('a', this).click(function(){

            $(this).next().slideToggle(o.speed);

        });

        return this;

    };
})(jQuery);
JeremySpouken
Appreciate the heads up Jeremy, but that doesn't seem to be the issue.As for "common patterns in UI design", I'm more concerned about the individual user experience than conforming to imaginary best practices that, for this type of user control, hardly falls into the category of established. This design is based on actual user testing, so I'm quite confident it's the right design decision.But I digress. I'm at a loss as to what's causing this bug. The first toggler seems to be the only one getting any love in IE, and once toggled, it controls the last sub menu and the last sub menu only
Greg-J
A: 

Solution found.

The issue was that I wasn't specifically cycling through each list element found. I reworked the jQuery a bit to put everything inside an $.each() function and now all seems well. I'm not sure if this is the most efficient way of doing it now though, so if anyone see's any room for improvement, please let me know.

http://jsbin.com/edago3/8/

 (function($){

    $.fn.extend({

        gMenu: function (options) {

            var o = $.extend({
                speed: "fast"
            }, options);

            // Get this ul's decendant ul's
            var $ul = $('ul', this);

            // Hide decendant ul's that are not active
            $ul.not('.gm-active ul').hide();

            // loop through this lists list elements
            $.each($(this).children('li'), function(){

                // Create toggler elements for the first decendant li's only
                $(this).children('a').after(
                  $('<span>', { html: "toggle", 'class': "gm-toggler"})
                );

                // find the togglers and hide them
                var $toggler = $(this).find('.gm-toggler').hide();

                // Only show the toggler when a menu item is hovered
                $(this).hover(function(){
                   $('.gm-toggler', this).toggle();
                });

                // Only show one child menu at a time. Never collapse them all
                $($toggler, this).click(function(){

                    // get this togglers parent ul
                    var $current = $(this).parent().find('ul');

                    // slide all other child menu up and remove its .active class
                    $($ul).not($current).slideUp(o.speed)
                        .parent().removeClass('gm-active');

                    // Stop sliding this togglers menu if it is animating and slide it the other direction
                    $current.stop(false,true).slideToggle(o.speed);

                    // If this togglers menu is active ...
                    $(this).parent('li').toggleClass('gm-active');
                });
            });
        }
    });
})(jQuery);
Greg-J
I was just getting back to this, and the only thing I noticed so far was that the "click" event was triggering 10+ times on every click in IE, but only once in other browsers.
Ryley