setTimeout () и настройка параметров - PullRequest
10 голосов
/ 02 марта 2011

У меня есть некоторый код jQuery, который выглядит следующим образом:

$('.mainNav2 > li').mouseleave(function(){
    var someNum = Math.random();
    $(this).attr('id', someNum);
    var t = setTimeout("HideMenu(someNum)", 200);
    $('li.clicked').mouseenter(function() {
        clearTimeout(t);
    });
});

function HideMenu(id) {
    $('#'+id).removeClass('clicked');
}

Его цель - скрыть мега-меню при отпускании мыши, но также учитывать случайные листья мыши, используя setTimeout за 300 миллисекунд. Если пользователь возвращает указатель мыши в li в течение 300 миллисекунд, меню не скрывается, потому что вызывается clearTimout (t).

Проблема в том, что когда пользователь НАЧИНАЕТСЯ отключить мышь, функция в setTimout не вызывается. Согласно этой странице: http://www.w3schools.com/js/js_timing.asp мой синтаксис правильный, но я могу вызвать функцию HideMenu только из setTimeout, если я напишу ее так:

var t = setTimeout(HideMenu, 300);

Почему это не работает так, как написано, где я могу передать переменную в функцию в качестве параметра?

Ответы [ 7 ]

13 голосов
/ 19 декабря 2012

Хотя помеченный правильный ответ является одним из способов достижения этого ... Я не верю, что он правильный.

См. Прилагаемую скрипку JS: http://jsfiddle.net/PWHw3/

То, что мы здесь делаем, это:

setTimeout(function, timeout, param);

Пример:

var test = function(a){
    var b = (a) ? a : "fail";
    alert(b);
};
setTimeout(test, 500, "works");

Это работает для меня и устраняет необходимость проходить через две функции.

13 голосов
/ 02 марта 2011

Чтобы заставить это работать, и делайте это без использования мерзкой eval-версии изменения setTimeout:

var t = setTimeout("HideMenu(someNum)", 200);

к этому:

var t = setTimeout(function(s) {  
                 return function() { HideMenu(s) } }(someNum), 200);

Таким образом, вы передаете значение someNum в переменную s в области действия setTimeout.

3 голосов
/ 02 марта 2011

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

  • функция отпускания мыши

    • присвоить переменной someNum значение. someNum ограничен текущая функция
    • установить таймер для оценки строки "HideNum(someNum)" через 200 мс.
    • Завершить функцию, выйти из области действия
  • 200 мс проходит

  • "HideNum(someNum)" оценивается. Он должен выдать исключение, так как переменная someNum не определена. Вот почему HideNum не вызывается - проверьте наличие ошибок в вашей консоли.

Вам нужна ссылка на функцию, которая будет держать ваш someNum в области видимости (через замыкание - о котором вы, возможно, захотите прочитать).

setTimeout(function() { 
  HideNum(someNum); 
}, 200);

Документация по Mozilla станет лучшим справочником по JavaScript. Вот window.setTimeout документы.

2 голосов
/ 02 марта 2011

Что вы хотите сделать, это создать анонимную функцию и вызвать ее при вызове setTimeout.

setTimeout(function() { HideMenu(someNum) }, 200);

Если я правильно понимаю, что вы хотите сделать, вам не нужно беспокоиться об установке идентификатора и всего такого. Примерно так должно это сделать:

$('.mainNav2 > li').mouseleave(function() {
  var $this = $(this);
  var t = setTimeout(function() {
    $this.removeClass('clicked');
  }, 200);
  $('li.clicked').mouseenter(function() {
    clearTimeout(t);
  });
});
1 голос
/ 12 декабря 2017

Это работает и может быть использовано внутри цикла.

var x = "OK";
setTimeout(alertOK.bind(null,x), 3000);
x = "Would be WRONG";
console.log("before timeout:", x);

function alertOK(x){
	console.log("after timeout:",x);
}
1 голос
/ 02 марта 2011

Есть и другие способы сделать это.

1. Использование анонимной функции (рекомендуется)

setTimeout(function() {
    hideMenu('someNum');
}, 200);

2. setTimeout и setInterval имеют скрытую функцию: вы можете указать параметры по истечении времени ожидания.

setTimeout(hideMenu, 200, params);

Поскольку вы уже используете jQuery, вам не следует создавать отдельную функцию, а вместо этого расширять прототип jQuery, например:

jQuery.fn.hideMenu = function() {
    this.removeClass('clicked');
    return this;
};

Использование:

$('#num').hideMenu();

Обратите внимание, что вам не нужно передавать this в jQuery при расширении его прототипа, потому что он уже передан в jQuery. И return this; требуется для сохранения способности цепочки.

Если вы выберете этот способ, вам нужно использовать анонимные функции, более простого способа нет.


Обновление

Для этого уже есть плагин: jQuery.hoverIntent(). Не нужно делать это самостоятельно. Его легко использовать, просто замените событие mouseleave на следующее:

$('#someNum').hoverIntent(jQuery.noop, function() {
    // your function goes here
});

Важно сделать это таким образом, поскольку первый - это обработчик mouseenter, а второй - обработчик mouseleave. jQuery.noop - пустая функция, по сути, такая же, как function() {}.

0 голосов
/ 02 марта 2011

Поскольку вы пишете строку, которая не так умна, как фактическое замыкание.

$('.mainNav2 > li').mouseleave(function(){
    var someNum = Math.random();
    $(this).attr('id', someNum);
    var t = setTimeout(function() { HideMenu(someNum); }, 200);
    $('li.clicked').mouseenter(function() {
        clearTimeout(t);
    });
});

function HideMenu(id) {
    $('#'+id).removeClass('clicked');
}
...