Search code examples
jqueryclickmouseeventconflictjquery-events

Lag problem with jQuery focus() and blur() events


I'm attempting to create a navigation menu that uses some jQuery. I wanted keyboard users to be able to have the same experience as mouse users, so I'm duplicating the functionality found in my hover() event handler in my focus() and blur() event handlers. For some reason, this is causing noticeable lag in Firefox and IE when the user clicks on a link, which does not occur when the focus() and blur() code is taken out. How can I go about speeding this up? I've done as much optimizing as my limited javascript knowledge will permit, but I haven't seen any "speedup", so I'm thinking it's might be related to how these browsers handle the events.

Is there anything major I'm overlooking? Or are there any alternative ways to retain accessibility for keyboard users while not using these events?

        var statePad=0;

            function stateChanger(ourStatePad) {
                //the purpose of this function is to translate the current state of the menu into a different graphical representation of the menu state.
                var tempVar=ouStatePad;
                var tempArray = new Array;
                tempArray[5]=0;
                for (var x=0;x < 5;x++) {
                    tempArray[x]=tempVar % 10;
                    tempVar=(tempVar-tempArray[x])/10;
                }
                for (var arrayIndex=4;arrayIndex>=0;arrayIndex--) {
                   //Calculate the proper position in our CSS sprite, based on the each link's state, as well as it's neighbors'.
                    $(".block").eq(4 - arrayIndex)
                    .css(
                        "background-position",
                        //x-position
                        ((4 - arrayIndex) * -100) + "px " + 
                        //y-position
                        (tempArray[arrayIndex] + ((3 * tempArray[(arrayIndex) + 1]) * -30))) + "px";
                }
            }


        function hoverState(index,sign) {
            var placeholder=Math.pow(10,4-index);

            if (statePad != placeholder*2)
                statePad += (placeholder * sign);
            stateChanger(statePad);
}

        .click(function() {
            var index=$("#navbar a").index(this);
            statePad=Math.pow(10,(4-index))*2;
            stateChanger(statePad);
            $(".active").removeClass("active");
            $(this).addClass("active");
        })


        .hover(
            function () {
                hoverState($("#navbar a").index(this),1);
            },
            function () {
                hoverState($("#navbar a").index(this),-1);
            });

        $("#navbar a").focus( 
            function() {
                hoverState($("#navbar a").index(this),1);
            }
        );

        $("#navbar a").blur( 
            function() {
                hoverState($("#navbar a").index(this),-1);
            }
        );  
    });

You can check it out here


Solution

  • I solved this by complete accident, sort of. I realized that my problem wasn't really the lag, that was just a symptom of a "conflict of events".

    The problem as far as I can tell is that focus() is triggered either by tabbing over to a link or a mousedown() on an element that can receive focus. So, every time a link is clicked on, it's getting focus. But, a click() event isn't complete until the mouse is released. So the effect that I was seeing in Firefox and IE was a result of a slight delay between mousedown() and mouseup(). I tried just swapping out the .click() event handling in my code to mousedown(), and since it's just a single event that I was watching out for I decided to integrate that into my hoverState() function. I ended up with this:

    function hoverState(e) {
        var index = $(e.target.parentNode).prevAll().length;
        if (e.type == 'mousedown') {
            statePad=Math.pow(10,(4-index))*2;
            $(".active").removeClass('active');
            $("#"+ e.target.id).addClass('active');
    }
    else {
       var sign = (e.type === 'mouseenter' || 
                     e.type === 'focus')? 1 : -1;
     var placeholder=Math.pow(10,4-index);
        if (statePad != placeholder*2)
            statePad += (placeholder * sign);
     $('h1').text(statePad + " " + e.type);
        }
        stateChanger(statePad);
    }
    
    $("#navbar a").bind("mouseenter mouseleave focus blur mousedown", hoverState);
    

    However, this caused some bizarre behavior that screwed up the statePad variable. I reverted back to the code that Russ Cam provided me, and started rethinking things. I tried it out in Opera, which I hadn't done yet, and it worked fine. I tried it in Safari and Chrome, and they worked fine as usual. I tried it in Firefox, just trying to get a handle on what makes it different, and...it was working!

    I looked back at my code, and it turns out that I was still binding the hoverState function to the mousedown() event. I'm not exactly sure why this works, but it does. It fixes the problem in IE, as well. Strangle, it causes a new issue in Chrome, it's so minor that I'm not even going to worry about.

    I don't think I would have been able to resolve this without Russ' help, so I want to thank him again for all of his help in this.