Проблема задержки с событиями jQuery focus () и blur () - PullRequest
4 голосов
/ 29 октября 2009

Я пытаюсь создать меню навигации, которое использует некоторые jQuery. Я хотел, чтобы пользователи клавиатуры имели возможность работать с мышью так же, как и я, поэтому я дублирую функциональность моего обработчика событий hover() в моих обработчиках событий focus() и blur(). По какой-то причине это вызывает заметную задержку в Firefox и IE, когда пользователь нажимает на ссылку, что не происходит, когда код focus() и blur() убирается. Как я могу ускорить это? Я сделал столько оптимизации, сколько позволят мои ограниченные знания JavaScript, но я не видел никакого «ускорения», поэтому я думаю, что это может быть связано с тем, как эти браузеры обрабатывают события.

Есть что-то важное, что я пропускаю? Или есть какие-то альтернативные способы сохранить доступность для пользователей клавиатуры, не используя эти события?

        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);
            }
        );  
    });

Вы можете проверить это здесь

Ответы [ 2 ]

3 голосов
/ 29 октября 2009

В вашем коде много ненужного удлинения цепочки областей действия , и более длинная цепочка областей действия займет больше времени. Это может быть сокращено до следующего

$("navbar a").click(blah) 
             .hover(foo,bar)
             .focus(foo)
             .blur(bar);

Надеюсь, это должно привести к меньшему отставанию. Если вы все еще видите заметное отставание после внесения этого изменения, пожалуйста, опубликуйте код для функций обработчика событий, так как могут быть улучшения, которые также могут быть внесены в этот код.

EDIT:

В ответ на ваш комментарий вы можете получить индекс в функции, используя переданное в event свойство target объекта, которое будет элементом, для которого было инициировано событие. Таким образом, чтобы получить индекс элемента <a> во всех элементах <a> в <ul> с идентификатором navbar , мы можем использовать тот факт, что каждый <a> содержится в <li>, поэтому индекс в каждом случае будет одинаковым. Имея это в виду, event.target будет элементом <a>, на котором возникает событие click, event.target.parentNode будет родительским элементом <a>, который является <li>

Чтобы получить индекс, вы можете использовать

function hoverState(e) { 
    // get the index of the <a> element, which will be the same
    // as the index of the <li> that contains it in the <ul>
    //
    var index = $(e.target.parentNode).prevAll().length; 
    //
    // get the sign
    var sign = (e.type === 'mouseenter' || e.type === 'focus')? 1 : -1;
} 

Это устранит необходимость в обработчиках событий анонимных функций, оборачивающих hoverState.

Вот немного переработанного кода

var statePad=0;

// the purpose of this function is to translate 
// the current state of the menu into a different 
// graphical representation of the menu state.
//
function stateChanger(ourStatePad) {

    var tempVar=ourStatePad;
    var tempArray = [0,0,0,0,0];
    for (var x=0;x < 5;x++) {
        tempArray[x]=tempVar % 10;
        tempVar=(tempVar-tempArray[x])/10;
    }
    // Calculate the proper position in our CSS sprite, 
    // based on the each link's state, as well as it's neighbors'
    //
    var arrayIndex=4;
    while (arrayIndex--) {

        $("#rightpostheader div.block").eq(4 - arrayIndex)
            .css(
                "backgroundPosition",
                //x-position
                ((4 - arrayIndex) * -100) + "px " + 
                //y-position
                (tempArray[arrayIndex] + ((3 * tempArray[(arrayIndex) + 1]) * -30))) + "px";
    }

}


function hoverState(e) {
    var index = $(e.target.parentNode).prevAll().length;
    var sign = (e.type === 'mouseenter' || 
                e.type === 'focus')? 1 : -1;
    var placeholder=Math.pow(10,4-index);

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

$("#navbar a")
    .click(function(e) {
        // might be able to rework this into using hoverState too
        var $this = $(e.target);

        // get the index of the parent <li>
        var index= $this.parent().prevAll().length;

        statePad=Math.pow(10,(4-index))*2;

        stateChanger(statePad);

        $("#navbar a").removeClass('active');
        $this.addClass('active');
    })
    .bind("mouseenter mouseleave focus blur", hoverState);  
0 голосов
/ 06 ноября 2009

Я решил это совершенно случайно, вроде. Я понял, что моя проблема на самом деле не в отставании, а в симптоме «конфликта событий».

Проблема, насколько я могу судить, заключается в том, что focus() вызывается либо переходом на ссылку, либо mousedown() на элементе, который может получить фокус. Таким образом, каждый раз, когда на ссылку нажимают, она получает фокус. Но событие click() не завершено, пока мышь не будет отпущена. Так что эффект, который я видел в Firefox и IE, был результатом небольшой задержки между mousedown() и mouseup(). Я попытался просто поменять обработчик событий .click() в моем коде на mousedown(), и, поскольку это всего лишь одно событие, которое я ожидал, я решил интегрировать это в свою функцию hoverState (). Я закончил с этим:

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);

Однако, это вызвало странное поведение, которое испортило переменную statePad. Я вернулся к коду, который предоставил Расс Кэм, и начал переосмысливать вещи. Я попробовал это в Опере, что я еще не сделал, и это работало отлично. Я попробовал это в Safari и Chrome, и они работали нормально, как обычно. Я попробовал это в Firefox, просто пытаясь понять, что отличает его, и ... это работало!

Я оглянулся на свой код, и оказалось, что я все еще связывал функцию hoverState с событием mousedown(). Я не совсем уверен, почему это работает, но это работает. Это исправляет проблему и в IE. Задушить, это вызывает новую проблему в Chrome, это настолько незначительно, что я даже не собираюсь беспокоиться.

Я не думаю, что смог бы решить эту проблему без помощи Русса, поэтому я хочу еще раз поблагодарить его за всю его помощь в этом.

...