2015-05-22 3 views
3

У меня есть сценарий, который отображает вопросы из массива и переключается с одного вопроса на следующий, используя событие .click на двух кнопках: предыдущем и следующем. Когда я загружаю страницу, селектор $(":radio").change работает нормально, но когда я нажимаю на предыдущий или следующий, он перестает работать.Почему это событие jQuery .change перестает работать после события .click

Я попытался изменить $("#previous) на console.log, чтобы узнать, был ли виновником .click, и он работает. Я предполагаю, что что-то не так с моей функцией отображения, но я не могу понять, почему.

Адрес jsFiddle. А вот проект по GitHub

Вопросы массива

var quizQuestions = 
[{ 
    question: "1. Who is Prime Minister of the United Kingdom?", 
    choices: ["David Cameron", "Gordon Brown", "Winston Churchill", "Tony Blair"], 
    correctAnswer:0 
}, 
{ 
    question: "2. Who is Prime Minister of the United Kingdom?", 
    choices: ["David Cameron", "Gordon Brown", "Winston Churchill", "Tony Blair"], 
    correctAnswer:2 
}, 
{ 
    question: "3. Who is Prime Minister of the United Kingdom?", 
    choices: ["David Cameron", "Gordon Brown", "Winston Churchill", "Tony Blair"], 
    correctAnswer:1 
}]; 

JavaScript код (JQuery)

$(document).ready(function(){ 
    var all = quizQuestions, 
     q = $("#question"), 
     c = $("#choices"), 
     ans = [], 
     current = 0; 

    display(current); 

    function display (id) { 
     var question = all[id].question; 
     var choices = all[id].choices; 

     q.html(question); 
     c.html(function(){ 
      var output = "<form>"; 
      for (var i = 0; i < choices.length; i++) { 
       output += "<input type='radio' name='question" +id 
       + "' value='" + choices[i] + "'> " 
       + choices[i] + "<br>"; 
      }; 
      output += "</form>" 
      // console.log(output); 
      return output; 
     }); 
    }; 

    function changeDisplay (dir) { 
     if (dir == 'next' && current < all.length-1) current++; 
     if (dir == 'prev' && current > 0) current--; 
     display(current); 
    } 

    function answerQ (q, a) { 
     ans[q] = a; 
     console.log(ans); 
    } 

    $(":radio").change(function(){ 
     answerQ($(this)[0].name, $(this).val()); 
    }); 

    $("#previous").click(function(){ changeDisplay('prev'); }); 
    $("#next").click(function(){ changeDisplay('next'); }); 

    // console.log("all.length: " + all.length + " | ans: " + ans + " | current: " + current); 
}); 
+0

Существует 'console.log', что я не мог показать на скрипке. Предполагается, что будут отображены «имя» и «значение» выбранного вопроса/ответа. –

ответ

5

Это, скорее всего, потому что событие не делегирован. Когда страница загружается, вы связываете события кликов. Но по мере того, как dom управляется (элементы удаляются), события удаляются. Для этого вам нужно будет использовать delegate.

Как это:

$('.container').on('change', ':radio', function(){ 
    answerQ($(this)[0].name, $(this).val()); 
}); 

Edit: Я хотел бы предложить вам сохранить или скрыть ответы, а не перезаписывать их. Это упростит выполнение предыдущей функции.

+1

Он интересовался обработчиком изменений, а не обработчиком кликов, но применяется то же самое. –

+0

Я думал, что '$ (" # previous "). Click (function() {changeDisplay ('prev');});' было сокращено за то, что вы написали. Я ошибаюсь? –

+1

@jeanpier_re Это не то же самое. $ ("# previous"). Нажмите bind на элемент с «предыдущим» как id. Где, когда делегат «on» привязывается к элементу body и прослушивает событие click на #previous. Чтобы предыдущий можно было удалить, а событие все еще подключено. –

3

Похоже, что когда вы переходите к следующему набору вопросов, вы заменяете HTML на странице для каждого переключателя. Однако вы никогда не назначаете обработчики кликов новым переключателям. Сначала вы делаете это во всех переключателях, когда страница загружается, но после изменения дисплея вы не повторно применяете обработчик кликов к переключателям.

В обработчике document.ready у вас есть это около дна:

$(":radio").change(function(){ 
     answerQ($(this)[0].name, $(this).val()); 
    }); 

Но в функции отображения (вызывается, когда вы выбираете предыдущий или следующий) у вас есть это:

 c.html(function(){ 
      var output = "<form>"; 
      for (var i = 0; i < choices.length; i++) { 
       output += "<input type='radio' name='question" +id 
       + "' value='" + choices[i] + "'> " 
       + choices[i] + "<br>"; 
      }; 
      output += "</form>" 
      // console.log(output); 
      return output; 
     }); 

Какие не добавляет обработчик кликов к любому из новых параметров ввода.

+0

Хорошая точка. Как вы предлагаете мне это сделать? Я не совсем уверен, как подойти к этому. –

+1

Есть несколько способов сделать это. Я бы добавил класс в форму и затем использовал селектор в моей инструкции jQuery, чтобы применить изменение. $ (". Options: radio"). Change (function() { answerQ ($ (this) [0 ] .name, $ (this) .val()); )); – monkey

+0

Хорошая идея, я буду работать в своем следующем рефакторе. –

0

Я бы переместил функцию отображения из метода обратного вызова для события $ (document) .ready(). IMHO, он принадлежит в глобальном пространстве, потому что он вызывается другими функциями в глобальном пространстве. Это просто чище, чем полагаться на Closure, чтобы сохранить ссылку на объект, скрытый в обработчике событий.

EDIT: Я предполагаю, что проблема заключается в использовании этого, без прохождения кода. Меняются вещи, если вы используете проход в объекте события в вашем обработчике .change() и используете его для получения ссылки на этот элемент?

+0

С функцией «on()», которая будет работать, но с «change()» это не так, если скрипт не находится под элементами, о которых идет речь на странице. DOM должен отобразить элемент, прежде чем вы сможете назначить таких прямых слушателей. Это та же проблема, что и чистый javascript, когда прослушивание полной загрузки документа гарантирует, что все элементы находятся в DOM. – SeanKendle

+0

Случайно удалил мой предыдущий комментарий. Спасибо за отзывы, я рассмотрю это. –

2

В этом случае проблема заключается в том, что вы добавляете к элементу слушателей, таких как «.click()» или «.change()» и т. Д., Вы только настраиваете прослушиватель для этого объекта.Чтобы создать слушатель, который применяется ко всем элементам, находящимся на данный момент на странице, а также к любым будущим элементам, созданным с помощью динамических средств (ajax, новые элементы, добавленные через javascript после завершения загрузки страницы), вы должны установить функцию «on» document:

Изменить то, что у вас есть здесь:

$(":radio").change(function(){ 
    answerQ($(this)[0].name, $(this).val()); 
}); 

к этому:

$(document).on("change", ":radio", function(){ 
    answerQ($(this).name, $(this).val()); 
}); 

(я не думаю, что [0] на $ (это) означает, что угодно, потому что в этот момент он будет применяться только к элементу, который был ju st изменено в любом случае).

Надеюсь, что это поможет!

Вот ссылка для чтения futher по теме: http://api.jquery.com/on/

+0

'$ (this) [0]' вытаскивает имя переключателя, поэтому я могу связать это с выбранным ответом. Кажется, это единственный способ добиться этого с помощью массива, который я получил. –

+0

Вы правы, извините, я забыл, что это список переключателей, так что технически они оба изменились. – SeanKendle

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