simple javascript JS event handler not working correctly - javascript

Below is my code and the instructions that went with it. Currently the responseText for an incorrect answer displays on page load and I can't change it to the correct answer.
// Declare a string variable called question and set it equal to a True/False question of your choice.
var question = "KD has sold his legacy at OKC for a championship ring at GS.";
// Declare a boolean variable called answer and set it equal to true or false (the answer to the question you asked above.)
var answer = true;
// Create a function called loadQuestion() that sets the value of the questionText element equal to the question variable you declared above.
function loadQuestion(){
document.getElementById("questionText").innerHTML = question;
}
// Create a function called checkAnswer(userAnswer) that takes one incoming parameter.
function checkAnswer(userAnswer){
if(userAnswer===answer){
document.getElementById("responseText").innerHTML = "That is correct!";
document.getElementById("responseText").className = "correctAnswer";
}
else if(userAnswer!==answer){
document.getElementById("responseText").innerHTML = "I'm sorry, that is
not correct.";
document.getElementById("responseText").className = "incorrectAnswer";
}
}
// ---> If your answer variable matches the incoming userAnswer parameter, write a success message in the element called responseText.
// ---> If your answer variable does NOT match the userAnswer parameter, write a failure message in the element called responseText.
// Create TRADITIONAL DOM EVENT HANDLERS for the "onclick" events for the three buttons.
var start = document.getElementById("startButton");
var truth = document.getElementById("trueButton");
var falsify = document.getElementById("falseButton");
// The Start button should trigger the loadQuestion method
start.onclick = loadQuestion;
// The True button should trigger the checkAnswer method with a value of "true"
truth.onclick = checkAnswer("true");
// The False button should trigger the checkAnswer method with a value of "false"
falsify.onclick = checkAnswer("false");

if you are comparing as triple equality === make sure that type is the same on both sides

it should be
<...>.onclick = function() {
checkAnswer("true");
}
instead of
<...>.onclick = checkAnswer("true");

Related

The value from a function returns undefined when passed through another function

I am currently building a quiz app that automatically prints out a random arithmetic quiz with a countdown timer. I must say I am scared of being blocked for asking this question but I decided to take the risk.
This is the block of code that prints out a question from the array of questions:
function generateQuestion() {
question.textContent = quizzes[Math.trunc(Math.random() * quizzes.length)];
const questionNumber = question.textContent;
return questionNumber;
}
This is the code block that evaluates the arithmetic problem in the array, solves and returns feedback:
const correctAnswer = eval(generateQuestion()); // A stand-alone code not inside any function. Reason explained below
function checkAnswer() {
console.log(correctAnswer);
if (correctAnswer === Number(answer.value)) {
feedback.textContent = "Correct!";
score++;
document.querySelector(".score").textContent = score;
answer.value = "";
} else {
feedback.textContent = "Wrong!";
}
}
At first, it scores you correctly when you input the correct answer but subsequent questions are marked wrong.
I tried debugging by printing values to the console then I found out this block of code: const correctAnswer = eval(generateQuestion()); was still holding the previous value. I could not place it into the function checkAnswer() as this continued to return undefined.
Is there a way to go around this or am I missing something?

Javascript: Calling a function from an array of objects

I have an array of objects and I am building a page with those objecs
The objects have an image, image will be clickable and I want to count the clicks per image.
I am trying to assign the value for clicks from each object to "tclicks" and than hand "tclicks" over to the onclick function to count the clicks separately.
I am not sure if that works.
My current problem is that the value appears as NaN when the onclick function gets executed.
I am aware that I need to assign a starting value to clicks.
I tried it in various places.
In the object, in the function outside of the function. Either the value was not counting up, or I got an error as the element was set to 0
Where can I assign the starting value?
This is the array
var things =
[
{img : "cat.jpg",
tclicks: "cleo_clicks",
id : "cleo"
},
{img : "shimi.png",
tclicks: "shimi_clicks",
id : "shimi"
}
]
This is how I am building the page
for ( var i= 0; i < things.length; i++){
var x = document.createElement("IMG");
x.setAttribute("src", things[i].img);
x.setAttribute(tclicks, things[i].clicks);
x.setAttribute("onclick", "countClicks(tclicks)");
document.body.appendChild(x);
}
And this is my onclick functions
function countClicks(clicks){
clicks ++;
console.log(clicks)
}
There's no reason you can't assign clicks=0 where you define the array of objects like this:
var things = [
{
img: "cat.jpg",
tclicks: "cleo_clicks",
id: "cleo",
clicks: 0
},
{
img: "shimi.png",
tclicks: "shimi_clicks",
id: "shimi",
clicks: 0
}
];
...but that's only half the problem.
In your click-increment function:
function countClicks(clicks) {
clicks++;
console.log(clicks);
}
...you will never affect a change on any object's click property because Javascript passes function parameters by value (https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value).
You need to pass in the entire object to the function and allow it to modify the property value instead:
function countClicks(someObject) {
someObject.clicks++;
console.log(someObject.clicks);
}
...which means you need to change how the function is called, something like:
x.setAttribute("onclick", "countClicks(things[i])");
Note: above assumes things array is global, otherwise you'll need to further refactor.
Final note, calling JS pass-by-value is simplifying a little. You ca dive into the issue more here: Is JavaScript a pass-by-reference or pass-by-value language?
Yep. Use an object and don't write it hard as onClick. It's too dangerous. You could override it. Use an eventListener instead.
function CountClicks(config){
var img = config.img;
var tclicks = config.tclicks;
var id = config.id;
var clicks = 0;
return{
countUp: function(){
clicks++;
},
countDown: function(){
clicks--;
},
getCount: function(){
return clicks;
}
}
}
x.counter = new CountClicks(things[i]);
x.addEventListener('click', function(){this.counter.countUp()});

Adding control ID's into Global Array variable

I am pretty sure I understand why this is not working. I think its because at the time the controls are trying to be found, the page has not rendered them yet.
Assuming that is the case, My question is how can I set these Arrays after the page has been fully loaded and still access them globally?
If I set the Arrays in the function my code works but it seems like a waste to find them like that as there will end up being 12 controls per array and the function really only needs to access 2 controls.
Here is some of my code:
//The controls in the following arrarys can not be found at this point of execution.
//If I put these Arrays into my function , the code works and the controls are found.
var RadRatingArray = [$find("<%= RadRating_Rating0.ClientID %>"), $find("<%= RadRating_Rating1.ClientID %>")];
var RadRatingNAArray = [$find("<%= RadRating_NA0.ClientID %>"), $find("<%= RadRating_NA1.ClientID %>")];
var RadTextBoxArray = [$find("<%= RadTextBox_Comment0.ClientID %>"), $find("<%= RadTextBox_Comment1.ClientID %>")]
//This function is called when the NA rating control is clicked. It clears the rating control and sets a number of other elemnts on the page.
function ClearRadRating_Rating(sender, args) {
var Number = sender.get_id().slice(-1);
var IntNum = parseInt(Number, 10);
if (sender.get_value() !== 0) {
RadRatingArray[IntNum].set_value(0);
}
SetSelectedRating(sender, RadTextBoxArray[IntNum], "Message" + [Number], "CallOut" + [Number]);
}
Thanks!

Value from Math.random is causing problems when I call the function inside itself

This script is giving me problems. I've re written it several times but I'm missing something.
'questions' array refers to my objects. These objects are different questions with 'a' always being the correct answer.
The script works fine up to this point. The idea is to increment 'n' from 0 to scroll through my array, and provide the next question when a correct answer is clicked. 'x' is always the correct answer (it always holds 'a').
So I can increment 'n' just fine on correct guess; however, when I call the function 'a()' again, (after my alert 'hi' part), it is causing problems. I want to increment n, and call a() so I get the next question. Then I want to place the correct guess (x) in a random place (ie position 0, 1 or 2.)
Grateful for any help.
var questions = [q0,q1,q2,q3,q4,q5,q6,q7];
var n = 0;
function a(){
var y;
var z;
var x = Math.floor((Math.random() * 3))
if(x == 0){y = 1; z = 2}else if(x == 1){y = 0; z = 2}else{y = 0; z = 1}
$("#question_holder").text(questions[n].q);
$(".answer_holder").eq(x).text(questions[n].a);
$(".answer_holder").eq(y).text(questions[n].b);
$(".answer_holder").eq(z).text(questions[n].c);
$(document).ready(function(){
$(".answer_holder").eq(x).click(function(){
alert("hi");
n++;
a();
/*this area needs to get the next question by incrementing
n, then generate a different value to x, to place it
in a different position. which it does. however,
in subsequent questions, you can click the wrong answer as if
it's correct, ie not 'a' or 'x'.*/
});
});
};
Your logic is a bit strange here.. what you are trying to do is register a new click event every time a() runs. I think you want to register one click event for all answer_holder elements, and in the event handler check which element this is and handle it accordingly
Notice the following:
$(document).ready(function(){ - the function defined in this handler is supposed to run once your page is loaded.. I don't think you want to use it inside a(). It is usually only used in global scope (outside all functions)
$(".answer_holder").eq(x).click(function(){ - this event handler registers your function depending on the value of x. I suggest you register an event handler for all $(".answer_holder"), without depending on x (in the global scope). And inside that event handler (in the function), you check which element triggered the event (using $(this) - it returns the element that was clicked)
You have the $(document).ready() in the wrong place. Try something like this (caveat: this is completely untested):
function setupQuestion(n) {
var x,y,z;
x = Math.floor((Math.random() * 3))
if(x == 0){y = 1; z = 2}else if(x == 1){y = 0; z = 2}else{y = 0; z = 1}
$("#question_holder").text(questions[n].q);
$(".answer_holder").eq(x).text(questions[n].a).data('answer', 'a');
$(".answer_holder").eq(y).text(questions[n].b).data('answer', 'b');
$(".answer_holder").eq(z).text(questions[n].c).data('answer', 'c');
}
$(document).ready(function() {
var n = 0;
$('.answer_holder').click(function() {
if ($(this).data('answer') === 'a') { // Or whatever is the correct answer
n++;
if (n < questions.length) {
setupQuestion(n);
} else {
// Do something else, the test is finished
}
}
return false;
});
setupQuestion(n);
});
Note that I am not comparing on the text() function but rather the data() function. This is because the text as displayed to the user might be decorated in some way, making comparisons difficult. Simply using the answer index 'a' 'b' or 'c' is clearer.

JS Ajax onreadystatechange is not defined although object exists

I am struggling with this for a day now, and going in circles and help would be greatly appreciated :-)
Abstract
Asynchronous ajax calling cgi with resolver and fqdn variables in order to return a dns resolution agains this pair. (returns the output of dig #resolver $fqdn)
Problem
In firebug I can see that get requests are being fired asynchronously and responses to the browser are as expected. However I cannot place the responses in correct divs in doc as onreadystatechange doesn't recognize the objects.
Side note
Apart from the fact that I am iterating through the array of objects It appears that they all are fired instantaneously even when delay between iterations is placed.
Below is a code with my comments
As resolver is an array I created an array of xmlhttprequest objects.
function resolve() {
var numofres = 6;
var arr = new Array;
arr[0] = "192.168.1.11";
arr[1] = "8.8.8.8";
arr[2] = "8.8.4.4";
arr[3] = "159.134.0.1";
arr[4] = "159.134.0.2";
var len = arr.length;
var ax = new Array(); //creating ax as an array
for (var i=0; i<=len; i++) { //iterating through the length of resolvers array
ax[i] = new XMLHttpRequest(); //assigning to each item in array new object
//alert(ax[i]); // shows that object exists
ax[i].onreadystatechange = function(){
/*===
problem is above - firebug will show:
**Uncaught TypeError: Cannot read property 'readyState' of undefined**
**ax.(anonymous function).onreadystatechangehello.cgi:30**
oddly it will still populate divs inner html with 'loading +1 '
albeit regardless of readystate code (can be 4 or anything else )
It perplexes me why i is thought as a function?
=====*/
// alert(i); //if this is enabled I will see readyState==4 populated correctly
if (ax[i].readyState != 4) {
document.getElementById('return_table_'+i).innerHTML="loading "+i;
}
if(ax[i].readyState == 4){
// get data from the server response
var response_ready=ax[i].responseText;
document.getElementById('return_table_'+i).innerHTML = response_ready;
}
}
ax[i].open("GET","av.pl?resolver=" + arr[i] +"&fqdn=google.com",true); //works
ax[i].send(null); //works
}
}
Your problem is an extremely common one. In JavaScript, variables are scoped at the function level, not at the block statement level. Thus, as you iterate through that loop with the variable "i", each function you create in the loop shares the same "i". Thus when the functions are actually called, the value of "i" will be what it is at the end of the loop — and that's a point beyond the end of the array!
To avoid the problem, you need to create those functions in another function. A clean way to do that is to have a separate local function:
function makeReadyStateHandler(i) {
return function() {
if (ax[i].readyState != 4) {
document.getElementById('return_table_'+i).innerHTML="loading "+i;
}
if(ax[i].readyState == 4){
// get data from the server response
var response_ready=ax[i].responseText;
document.getElementById('return_table_'+i).innerHTML = response_ready;
}
};
}
Then just call that function from the loop:
ax[i].onreadystatechange = makeReadyStateHandler(i);
By using a separate function like that, you assure that each handler function will have its own copy of "i" that's frozen at the correct point in the loop. The function will return a newly-created function as its result, which you'll use as the event handler.

Categories