2011-09-27 3 views
2

Я пытаюсь создать эффект аккордеона для документа, где, когда вы нажимаете на <h1> остальную часть документа (<div.container>) с переключателем, сдвигающимся вверх и сползающим вниз. Однако я все время сталкиваюсь с проблемой. Вот код:Неверный объект, на который ссылается при активации события click

HTML

<article> 
    <h1>Title</h1> 
    <div class="container"> 
     //... 
    </div> 
</article> 

<article> 
    //... 
</article> 

<article> 
    //... 
</article> 

CoffeeScript:

articles = $('article').toArray() 

for article in articles 
    #console.log $('.container', article).parent().attr('id') 
    $('h1', article).click -> 
     $('.container', article).slideToggle 'slow' 

Когда я использую article переменную сказать ... console.log Она вращается через статьи и печатает обратно свои идентификаторы. Но когда я нажимаю на любой из элементов <h1>, он всегда сбрасывает последние <article> с <div.container>.

Я думаю, что это потому, что переменная article хранится за пределами области цикла for в CoffeeScript, и щелчок не выполняется до тех пор, пока цикл не будет завершен.

Если это правда, Как я могу гарантировать, что на объект, на который ссылается объект, будет называться? Было бы лучше просто использовать цикл for i in [0...3] и просто ссылаться на массив напрямую? Проблема в другом? Спасибо за помощь!

Для тех, кто не знаком с CoffeeScript, вот-код, который компилируется (просто игнорировать _results переменного):

var articles 
articles = $('article').toArray(); 
_results = []; 

for (_i = 0, _len = articles.length; _i < _len; _i++) { 
    article = articles[_i]; 
    _results.push($('h1', article).click(function() { 
    return $('.container', article).slideToggle('slow'); 
    })); 
} 

ответ

3

Причину вы видите это поведение, потому что article ссылки в click события не оцениваются до тех пор, пока обработчик не будет запущен, и в этот момент времени он будет установлен на последнюю статью, которая была оценена в вашем цикле.

Это может работать немного лучше для вас (извините, не уверен в реализации кофе сценария):

$('article h1').click(function() { 
    $(this).next('.container').slideToggle('slow'); 
}); 
+0

Я пометю Rocco как ответ, так как он пришел первым, но всегда приятно видеть, как много решений для одной проблемы в JavaScript. Спасибо за помощь, ребята! – webdesserts

1

Вот еще один способ сделать это (в JavaScript/JQuery) по:

  1. Изменение первоначального выбора, чтобы выбрать все теги h1 в соответствии со статьей
  2. Использование this.parentNode получить контекст от щелчка
  3. Тогда посмотрите для .container объект под этим общим родителем.

Таким образом, нам не нужно сохранять какое-либо состояние по клику, поскольку мы можем просто найти контейнер, который имеет один и тот же родительский элемент с элементом, на который нажата кнопка. Этот метод также не зависит от точной позиции объекта контейнера относительно h1, на который нажата кнопка - ему просто нужен контейнер для совместного использования одного и того же родителя, чтобы он был более гибким в будущем макете.

$('article h1').click(function() { 
    $('.container', this.parentNode).slideToggle('slow'); 
}); 
0

Я ничего не знаю о Coffeescript, но скомпилированный Javascript имеет довольно очевидную ошибку.

Эта линия:

article = articles[_i]; 

Следует читать так:

var article = articles[_i]; 

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

Надеюсь, есть способ указать, что в Coffeescript, но кажется довольно ужасным, что для статьи в статьях не делает это автоматически.

+0

На самом деле, CoffeeScript создает объявление 'var article'; Майкл просто пропустил это. Это, однако, не решает проблему, потому что, как указывает Рокко, из обратного вызова 'click',' article' будет иметь свое значение с конца цикла. –

2

Ответ Рокко правильный. Позвольте мне рассказать об этом:

Это общая область путаницы: только функции создают область видимости в JavaScript (и CoffeeScript), а это означает, что когда вы выполняете что-то асинхронное в цикле, вы должны помнить о «захвате» переменная. Предпочтительный способ сделать это в CoffeeScript это с do синтаксиса, который позволяет писать

for article in articles 
    do (article) -> 
    $('h1', article).click -> 
     $('.container', article).slideToggle 'slow' 

, который составляет в эквиваленте

for article in articles 
    ((article) -> 
    $('h1', article).click -> 
     $('.container', article).slideToggle 'slow' 
)(article) 

Таким образом, каждый из click обратного вызова видит свою собственную итерацию article , а не тот, который изменяет значение при прохождении цикла. Об этом кратко расскажу в статье PragBub A CoffeeScript Intervention.

+0

+1 для решения CoffeeScript и статьи – webdesserts

Смежные вопросы