Why does this jQuery.change event stop working after .click event

I have a script that displays questions from an array and switches from one question to the next using an event .click

on two buttons: previous and next. When I load the page, the selector $(":radio").change

works fine, but when I click on the previous or next it stops working.

I tried changing $("#previous)

to console.log to see if it was the .click

culprit and it seems to work. I am guessing there is something wrong with my display function, but I cannot figure out why.

Here is a jsFiddle . And here is the GitHub project

Array of requests

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 code (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);
});

      

+3


source to share


4 answers


This is most likely because the event is not delegated. When the page loads, you bind click events. But as the dom is managed (items are removed) events are removed. You need to use a delegate for this .

Like this:



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

      

Edit: I suggest you keep or hide the answers instead of overwriting them. This will simplify the previous function.

+5


source


It looks like when you go to the next set of questions, you are replacing the HTML on the page for each radio button. However, you never assign click handlers to new radio buttons. First, you do this on all radio buttons on page load, but after you change the display, you don't reapply the click handler to the radio buttons.

In your document.ready handler, you have this at the bottom:

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

      



But in the display function (called when the previous or next is selected) you have this:

        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;
        });

      

Which doesn't add a click handler to any of the new input parameters.

+3


source


This is where the problem arises, when you add listeners like ".click ()" or ".change ()" etc.) to an element, you are only setting up a listener for that object. To create a listener that applies to all elements currently on the page, as well as any future elements that are dynamically created (ajax, new elements added via javascript after the page has finished loading), you must set the document

level function " on ":

Change what you have here:

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

      

:

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

      

(I don't think the [0] on $ (this) means anything, because at that point it only applies to the element that was just changed).

Hope it helps!

Here's a link for further reading on the topic: http://api.jquery.com/on/

+2


source


I would move the display function out of the callback method for the $ (document) .ready () event. IMHO it belongs in global space because it is called by other functions in global space. It's just cleaner than relying on Closure to keep the object reference hidden in the event handler.

EDIT: I guess the problem is using this without stepping over the code. Do things change if you use a pass on an event object in your .change () handler and use it to get a reference to an element?

0


source







All Articles