Подтвердить что ты не робот

Закрытие Javascript ведет себя по-разному, когда связано с событием

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

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 UI:

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

И приходит безумие. Я ожидаю, что каждый "аккордеон" на странице будет запускать lazyLoadGrid() ровно один раз, когда этот аккордеон будет сначала открыт. Вместо этого обратные вызовы замыкания, похоже, ведут себя так, как будто все они ссылаются на одну и ту же копию "ran". lazyLoadGrid() запускает первый раз, когда я открываю любой аккордеон, а затем никогда не запускается снова для любого другого аккордеона. Запись значения pre-condition из "ran" показывает, что он "true" каждый раз, когда я нажимаю любой аккордеон после первого.

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

4b9b3361

Ответ 1

Проблема:

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

http://jsfiddle.net/cTz4F/

Решение:

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

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

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

Ответ 2

Как насчет:

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

// test 
var foo = function(){
    console.log('bar');
}
foo = runOnce(foo);
foo(); // bar
foo();
foo();

Ответ 3

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

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

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

- изменить -

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