Закрытие Javascript ведет себя по-разному при привязке к событию - PullRequest
12 голосов
/ 01 марта 2012

Я пытаюсь использовать замыкание, чтобы функция могла выполняться только один раз.Звучит просто, и это работает так:

function runOnce(fn)  // returns copy of fn which can only execute once
{
    var ran = false;

    return function() 
    {
        if (!ran)
        {
            fn();
            ran = true;
        }
    };
}

Я проверил функцию следующим образом:

function lazyLoadGrid(event, ui)
{
    alert('hi');
}

var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);

test1();
test2();

test1();
test2();

И она работает, как и ожидалось - «привет» получает предупреждение ровно дважды.

Но затем я пытаюсь использовать runOnce (lazyLoadGrid) в качестве обратного вызова для события пользовательского интерфейса jQuery:

$('.accordion').each(function() 
{ 
    $(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});

И наступает безумие.Я ожидаю, что каждый «аккордеон» на странице будет запускать lazyLoadGrid () ровно один раз, когда этот аккордеон впервые открывается.Вместо этого обратные вызовы замыкания, похоже, ведут себя так, как будто все они ссылаются на одну и ту же копию run.lazyLoadGrid () запускается в первый раз, когда я открываю любой аккордеон, а затем никогда больше не запускается для любого другого аккордеона.Запись предусловия 'run' показывает, что оно 'true' каждый раз, когда я нажимаю на любой аккордеон после первого.

Как это можно объяснить?Возможно, стоит отметить, что у меня есть нечетная страница с вложенными аккордеонами и несколькими вкладками jQuery UI, каждая из которых содержит аккордеоны.Что еще хуже, когда я переключаю вкладки, закрытие фактически запускается на первом открытом аккордеоне любой данной вкладки.Любой совет очень ценится.

Ответы [ 3 ]

1 голос
/ 01 марта 2012

Как насчет:

function runOnce(fn) {
    return function(){
      fn();
      fn = function(){};
  }
}

// test 
var foo = function(){
    console.log('bar');
}
foo = runOnce(foo);
foo(); // bar
foo();
foo();
1 голос
/ 02 марта 2012

Проблема:

Я полагаю, что проблема, с которой вы сталкиваетесь, заключается в том, что то, что вы считаете «аккордеоном», на самом деле является «панелью». Аккордеон состоит из всех панелей в группе. Похоже, вы хотите запустить его один раз для панели, а не один раз для аккордеона. Следующая демонстрация иллюстрирует концепцию, включая два аккордеона на странице. Обратите внимание, что lazyLoadGrid() запускается дважды, один раз для каждого аккордеона:

http://jsfiddle.net/cTz4F/

Решение:

Вместо этого вы хотите создать собственное событие и вызвать его на каждой панели. Затем вы можете воспользоваться встроенным в jQuery методом .one(), который приводит к тому, что обработчик событий вызывается ровно один раз для каждого элемента:

$('.accordion').accordion({
    autoHeight: false,
    change: function(e, ui) {
        ui.newHeader.trigger("activated");
    }
});
$('.accordion > h3').one("activated", lazyLoadGrid);

Рабочая демоверсия: http://jsfiddle.net/cTz4F/1/

0 голосов
/ 01 марта 2012

Я думаю, потому что функция ожидает указания параметра события. попробуйте это:

$('.accordion').each(function() { 
$(this).accordion({ autoHeight: false, change: runOnce(arguments[0],lazyLoadGrid) }); 
});

попробуйте напрямую использовать функцию lazyLoadGrid или, если вам нужно использовать runOnce, вы должны указать аргументы [0] (который является событием) как параметр в функции

- редактировать -

извините, я забыл поместить событие в функцию

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...