Blake Allen Blake Allen - 2 months ago 5
Javascript Question

JS Function Parameter Resets Itself

I am trying to create a simple question-answer "flash card" program. The user types what the word means in the opposite language. Everything is working fine, except for one thing:

When you click the button to go to the next level, it works fine. However, when you enter a correct answer, it goes back to level 1. Why is this? Is the next() function level parameter resetting itself back to 1? Please help!

fiddle

HTML:









</head>

<body>
<h1>Spanish</h1>
<div id="main">
<h1 id="topic_name">Subject Pronouns</h1>
<p>Type the word/phrase below in the opposite language</p>
<p>If there is an accent or ~ above a letter, put ^ before that letter. Example: diecis^eis</p>
<hr id="margin-bottom">
<h1 id="question"></h1>
<input id="answer_box"/>
<button id="next"></button>
<button id="answer">Show Answer</button>
</div>

</body>
</html>


JS:

$(document).ready(function(){

var lvl1=
[
[["I"],["yo"]],
[["you (formal)"],["usted"]],
[["you (informal)"],["t^u"]],
[["he"],["^el"]],
[["she"],["ella"]],
[["we (masculine)"],["nosotros"]],
[["we (feminine)"],["nosotras"]],
[["you all (formal)"],["ustedes"]],
[["you all (informal)"],["vosotros"]],
[["they (masculine or mixed)"],["ellos"]],
[["they (feminine)"],["ellas"]],
];

var lvl2=
[
[["yo"],["I"]],
[["usted"],["you (formal)"]],
[["t^u"],["you (informal)"]],
[["^el"],["he"]],
[["ella"],["she"]],
[["nosotros"],["we (masculine)"]],
[["nosotras"],["we (feminine)"]],
[["ustedes"],["you all (formal)"]],
[["vosotros"],["you all (informal)"]],
[["ellos"],["they (masculine)"]],
[["allas"],["they (feminine)"]],
];

var lvl3=
[
[["yo soy"],["I am"]],
[["tú eres"],["you (informal) are"]],
[["él es"],["he is"]],
[["ella es"],["she is"]],
[["usted es"],["you (formal) are"]],
[["nosotros somos"],["we are"]],
[["vosotros sois"],["you all (informal) are"]],
[["ellos/ellas son"],["they are"]],
[["ustedes son"],["you all (formal) are"]],
];

var lvl4=
[
[["I am"],["yo soy"]],
[["you (informal) are"],["t^u eres"]],
[["he is"],["^el es"]],
[["she is"],["ella es"]],
[["you (formal) are"],["usted es"]],
[["we are"],["nosotros somos"]],
[["you all (informal) are"],["vosotros sois"]],
[["you all (formal) are"],["ustedes son"]],
];

next(1);

function next(level){
random=(Math.floor(Math.random()*10));

switch(level){
case 1:
question=lvl1[random][0];
answer=lvl1[random][1];
$('#next').text("Level 2");
break;
case 2:
question=lvl2[random][0];
answer=lvl2[random][1];
$('#next').text("Level 3");
break;
case 3:
random=(Math.floor(Math.random()*9));
question=lvl3[random][0];
answer=lvl3[random][1];
$('#next').text("Level 4");
break;
case 4:
var random=(Math.floor(Math.random()*8));
question=lvl4[random][0];
answer=lvl4[random][1];
$('#next').text("Done");
break;
default:
alert("switch error");
}


$('#question').text(question);



$('#answer_box').keyup(function(){
if($(this).val()==answer){
$('#answer_box').attr("placeholder","");
$('#answer_box').val("");
next(level);
}
});



$('#next').click(function(){
next(level+1);
});



$('#answer').click(function(){
$('#answer_box').attr("placeholder",answer);
});


}//function next

});//end


CSS:

#main{
border:3px solid blue;
height:500px;
width:600px;
top:50%;
left:50%;
position:fixed;
margin-top:-230px;
margin-left:-300px;
border-radius:5%;
}

h1{
text-align:center;
}


p{
text-align:center;
}

#margin-bottom{
margin-bottom:80px;
}

#next{
display:block;
margin:0 auto;
}

#answer_box{
display:block;
margin:0 auto;
margin-bottom:20px;
}

#answer{
display:block;
margin:0 auto;
}

Answer

The problem is failing to declare variables and creating a closure by accident.

question= and answer= are creating window properties of the same name because they have not been declared.

level is a formal parameter of function next and is first set to 1 by the statement next(1)

The click functions are declared as nested functions within next and access the value stored in the level parameter (creating a closure when next() returns). They are also reapplied to the button elements each time next is called.

Proposed solution: declare question answer and level variables within the "on ready" IIFE. Declare and apply button event handlers once, outside the call to next. Maintain level using explicit code.


Update with details:

Remove

 next(1);

And replace with variable definitions and a call to next() with no parameter. Level is being maintained outside of the next function.

var question = "";
var answer = "";
var level = 1;
next();

Remove the level parameter from function next(level)... and replace with

function next(){
random=(Math.floor(Math.random()*10));

switch(level){
  case 1:
    question=lvl1[random][0];
    answer=lvl1[random][1];
    $('#next').text("Level 2");
    break;
  case 2:
    question=lvl2[random][0];
    answer=lvl2[random][1];
    $('#next').text("Level 3");
    break;
  case 3:
    random=(Math.floor(Math.random()*9));
    question=lvl3[random][0];
    answer=lvl3[random][1];
    $('#next').text("Level 4");
    break;
  case 4:
    var random=(Math.floor(Math.random()*8));
    question=lvl4[random][0];
    answer=lvl4[random][1];
    $('#next').text("Done");
    break;
  default:
    alert("switch error");
}

$('#question').text(question);


}//  end of function next body

This is largely the same as the code posted, but move keyup and next button handlers to follow function next body; don't define them inside it:

$('#next').click(function(){
   level = level + 1;  // increase level
   next();
});

$('#answer_box').keyup(function(){
  if($(this).val()==answer){
    $('#answer_box').attr("placeholder","");
    $('#answer_box').val("");
    next();  // stay on current level
  }
});

I haven't put code in to handle what happens when you click the next button when "done" is showing. The closure you asked about is created by registering keyup and click handler anonymous functions created inside the next function: the anonymous functions look for variable and parameter definitions within next, corresponding to their value when the anonymous function was created. If you are unfamiliar with the concept, please do a search on closures in javascript.

Comments