I've only started to learn how to refactor code and I'm failing at abstracting a simple function. I pass the parameters into checkAnwser and it works, but count is "lost"
I can't get/append "count" here:
jsfiddle
<input data-correctanswer="javascript" id="answer1" name="" type="text">This works fine (no special chars)
<br/>
<button id="btn1">check 1</button>
<br/>
<input data-correctanswer="jávascripté" id="answer2" name="" type="text">
<br/>
<button id="btn2">check 2</button>
<div style="border: 1px solid;" id="result"></div>
Javascript:
$(document).ready(function () {
var count;
$('#btn1').click(function () {
checkAnswer($('#answer1').data('correctanswer'), $('#answer1').val());
$('#result').append('result: ').append(count); <-- does not read count
}); // end of click
$('#btn2').click(function () {
checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
$('#result').append('result: ').append(count); // <-- does not read count
}); // end of click
function checkAnswer(coorAns, UserAnswer) {
var count = 0;
// var coorAns = $('input[type=text]').data('correctanswer');
// var UserAnswer = $('input[type=text]').val();
var mistakesAllowed = 1;
if (UserAnswer === coorAns) {
count = count + 2;
}
for (i = 0; i < coorAns.length; i++) {
if (coorAns.charAt(i) !== UserAnswer.charAt(i)) {
mistakesAllowed--; // reduce one mistake allowed
if (mistakesAllowed < 1) { // and if you have more mistakes than allowed
count = count + 1;
}
if (mistakesAllowed < 0) {
count = count - 2
break;
}
}
}
console.log('final count: ' + count);
return count;
}
});
What Adeneo said:
var count; //here it's defined.
$('#btn1').click(function () {
count = checkAnswer($('#answer1').data('correctanswer'), $('#answer1').val());
$('#result').append('result: ').append(count);
}); // end of click
$('#btn2').click(function () {
count = checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
$('#result').append('result: ').append(count);
}); // end of click
Your function checkAnswer returns a value called count. That value can be assigned to the variable count.
Probably the chain of thought you had was that assigning count in the function checkAnswer would also assign it to the variable count outside the function. However those two variables are in two different scopes and are not connected to eachother unless you assign the result of the function to the variable count outside the function.
To be more precise:
checkAnswer is in the same scope as the first count variable. That means you could do this:
var count = 0;
console.log(count); //will log 0.
function checkAnswer()
{
count = 1;
}
checkAnswer();
console.log(count); //will log 1.
However when you use the operator var inside a function it will create a variable that is bound to the scope of the function (it becomes a private variable) only accessible within that function. Unless you return it to a variable outside the scope of that function.
var count = 0;
console.log(count); //will log 0.
function checkAnswer()
{
var count = 1;
}
checkAnswer();
console.log(count); //will log 0.
More on scopes on Stack Overflow:
What is the scope of variables in JavaScript?
Bonus:
A little efficiency suggestion for your code
var count;
$('#btn1', '#btn2').click(function () {
var buttonId = $(this).attr("id").substr(4); //will result in 1 or 2
count = checkAnswer($('#answer' + buttonId ).data('correctanswer'), $('#answer' + buttonId ).val());
$('#result').append('result: ').append(count);
}); // end of click
This will reduce it to one generic function. So when you need to update your code you only need to update one function, not multiple instances of the same code.
Instead of
checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
use
var count=checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val()); //Stored returned value to count variable.
Now you can access returned value.
Here is your updated fiddle.
Related
I have been working on my second game, which works as follows:
There is a button and its position changes randomly every x seconds
The x seconds is controlled by setTimeout
Whenever the user clicks on the button their score increments by 10
When the score reaches 30 startGame calls Initialize, which creates a new button using JavaScript and pushes the button to an array called numOfBox
The control passes back to startGame with the updated numOfBox, which now has two buttons and changes the top/left coordinates every x seconds
The function is being called by setTimeout multiple times every x seconds instead of once every x seconds. How do I call the function only once every x seconds?
var box=document.getElementById('boxId');
var main=document.getElementById('Maincont');
var timeout=[];
var cnt=0;
var scr=0;
var levelCnt=30;
var divcnt=0;
var numOfbox=[];
var fcnt=0;
function createDiv(Name,Width,Height,Background,Margin,Padding) {
var t=t+divcnt;
divcnt+=1;
var Name=Name+divcnt;
var Nameid='boxId'+divcnt;
Name=document.createElement('button');
Name.id=Nameid;
console.log('IN CREATEDIV :-'+ Nameid+':::'+Name+' cnt '+ cnt);
Name.style.width=Width;
Name.style.height=Height;
Name.style.background=Background;
Name.style.margin=Margin;
Name.style.padding=Padding;
Name.style.boxSizing='border-box';
Name.style.position='absolute';
Name.style.top='10px';
Name.style.left='10px';
Name.style.color='white';
Name.style.textAlign='center';
Name.style.fontSize='15px';
Name.style.textDecoration='none';
Name.style.cursor='pointer';
Name.disabled=true;
t=document.createTextNode('HIT ME');
Name.appendChild(t);
Name.addEventListener('click', function() {
this.style.background='black';
scr+=10;
cnt+=10;
this.innerHTML= 'SCORE = ' +String(scr);
});
var b=document.getElementById('Maincont');
// FIRST HIT ME BOX IS READY NOW....
b.appendChild(Name);
// Array numOfbox loaded with all the Box id's.
numOfbox.push(Name.id);
console.log('PUSHING DIV:-'+ Name.id + " IN ARRAY :=" +numOfbox);
// creatediv is called when score is scr%30==0.
if (numOfbox.length > 1) {
return Name.id;
}
}
function Initialize() {
if (main.childNodes.length < 5) {
if ((cnt > 0) && (cnt %30 ==0)) {
var id='targetCont'+divcnt;
divcnt+=1;
boxId=createDiv(id,'130px','50px','black','0px','0px');
console.log('Inside Initialize:-'+id+' cnt '+ cnt);
cnt=0;
}
startGame();
}
}
function startGame() {
var d=new Date();
var t=d.getMinutes()+':'+d.getSeconds();
fcnt+=1;
console.log('TIME IS:-'+ t + " cnt:-" +fcnt+' INSIDE STARTGAME:-'+numOfbox+':'+numOfbox.length+ ' CNT'+cnt);
for (var i=0 ; i < numOfbox.length ; i++) {
if ((cnt > 0) && (cnt %30 ==0)) {
Initialize();
} else {
console.log('STARTING GAME FOR DIV:='+numOfbox[i]);
var box=document.getElementById(numOfbox[i]);
console.log(box);
box.disabled=false;
var max=500;
var min=10;
var topRand=(Math.floor(Math.random() * (max-min+1) + min));
var max=1200;
var min=10;
var leftRand=(Math.floor(Math.random() * (max-min+1) + min));
box.style.background='black';
box.style.top=topRand+'px';
box.style.left=leftRand+'px';
console.log('CNT:='+cnt);
timeout=setTimeout(startGame,10000,numOfbox);
}
}
}
function stopGame() {
clearTimeout(timeout);
console.log('IN STOPGAME:-'+timeout+' length '+timeout.length);
for (var i=0 ; i < numOfbox.length ; i++) {
console.log(timeout[i]);
clearTimeout(timeout[i]);
var box=document.getElementById(numOfbox[i]);
box.style.background='red';
box.style.top='10px';
box.style.left='10px';
}
timeout=[];
}
I would prefer to do this in JavaScript and not in ECMA5/6 or jQuery, or using arrow functions. I can provide the rest of the code if needed.
I noticed a couple of issues with your code.
You are calling setTimeout in a loop. So if you have, say, numOfbox.length === 15, setTimeout will be called 15 times around 10,000 ms from when you set it. Could this be the reason you're seeing the more calls to startGame than you thought?
I see the variable numOfbox, but since it's not declared in the startGame function I'll have to assume that it's in the parent scope. So, in the line where you do
timeout[i] = setTimeout(startGame, 10000, numOfbox);
Realize that since numOfbox is in a higher level scope and startGame does not take any parameters, the numOfbox parameter (the 3rd argument in setTimeout) is really not going anywhere: the numOfbox variable actually uses is coming from the parent scope. This may be ok, but you should consider what is happening here.
I want value of total in another function call.
function recalculateTotal(sc) {
var total = 0;
//basically find every selected seat and sum its price
sc.find('selected').each(function() {
total += this.data().price;
});
return total;
}
function call() {
var a = recalculateTotal(sc);
window.location.replace("Demo2.jsp?name=" + a);
}
It depends on the scope of variable sc. Is sc global? You can do the following:
var scInput = 'whatever';
var total;
function recalculateTotal(sc) {
var total = 0;
//basically find every selected seat and sum its price
sc.find('selected').each(function() {
total += this.data().price;
});
return total;
}
total = recalculateTotal(scInput);
function call(a) {
window.location.replace("Demo2.jsp?name=" + a);
}
call(total);
Your original code sample is fine so long as the variable sc and call(); are in the same scope. I think you might be confused by parameters vs. arguments. In your recalculateTotal function you define a parameter called 'sc'. However, when you call that function you need to send in an argument (the value of sc). So your original example is correct in the following instance
var sc = 'your-value'
function recalculateTotal(sc) {
var total = 0;
//basically find every selected seat and sum its price
sc.find('selected').each(function() {
total += this.data().price;
});
return total;
}
function call() {
var a = recalculateTotal(sc);
window.location.replace("Demo2.jsp?name=" + a);
}
call(sc);
It is correct in the above example because there exists a variable sc in the global scope that the call function has access to. The same is also true for this example:
var sc = 'your-value'
function recalculateTotal() {
var total = 0;
//basically find every selected seat and sum its price
sc.find('selected').each(function() {
total += this.data().price;
});
return total;
}
function call() {
var a = recalculateTotal();
window.location.replace("Demo2.jsp?name=" + a);
}
call();
The confusion you might have is that in your example you have an argument sc in recalculateTotal(sc), and in the call method the attribute you pass I to recalculateTotal(sc) is also called sc. This is correct notation and a very common practice, but it does confuse people who are not familiar with it
I am quite new to javascript and i have encounter problems about function call and the closure.
Below are the code that i tried in w3schools,
<!DOCTYPE html>
<html>
<body>
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
<script>
var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();
/*function add(test){
var counter = 0;
return function (test) {return counter += test;}
}*/
function myFunction(){
document.getElementById("demo").innerHTML = add(123);
//document.getElementById("demo").innerHTML = add().call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
</script>
</body>
</html>
It works fine for the current code, that every time i press the button, the number in the paragraph(id="demo") increment by 123.
But when i tried the commented code, which create a function with closure with exact same code, the paragraph value remain at 123 every onclick.
In this situation, i have a few questions to ask.
1.For the code :
var add = (function (test) {...}) ();
What is the usage of the last bracket? If we provide parameter to the last bracket, how can we use it in the declaration of the anonymous function of variable (var add)?
2.Why these two ways to define the function result in different result?
Thanks a lot, any help is appreciated.
EDIT
<!DOCTYPE html>
<html>
<body>
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
<script>
/*var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();*/
function add(test){
var counter = 0;
return function (test) {return counter += test;}
}
function myFunction(){
//document.getElementById("demo").innerHTML = add(123);
document.getElementById("demo").innerHTML = add().call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
</script>
</body>
</html>
It seems that the code run with no error occur, when i use the commented code for declaration of function, but just a bug that the integer of the paragraph didn't increment.
The reason you are getting 123 all the time is because every time you click on the button, you take a new inner function with a closure on counter variable with value 0; So value of counter always remains 0 and when you add 123 to 0 you get 123.
If you move closure part out of the event handler, you would get exact same result as in the first case.
Notice the line var inner = add(); //<---Notice this line. This would take the closure one time and subsequently you will keep increasing the value of counter.
Also, notice this line inside myFunction:
document.getElementById("demo").innerHTML = inner.call(this, 123);
Here we are calling the inner function that we had reference on earlier.
/*var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();*/
function add(test) {
var counter = 0;
return function(test) {
return counter += test;
}
}
var inner = add(); //<---Notice this line
function myFunction() {
//document.getElementById("demo").innerHTML = add(123);
document.getElementById("demo").innerHTML = inner.call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
() is to invoke the anonymous function you just declare, and it return another anonymous function. If you provide a parameter, it will be fed to your function (test). In this case, you don't actually need the first test parameter. It's just not used.
For your commented code, it returns a function. And the function is never invoked.
() The last bracket is to execute it.
The example u have shown is that of an IIFE.
IIFE are used to limit scope of all ur variable so that namespaces can be demarkcated.
//without params
(function(){
//...
})()
//-OR- (both are same)
(function(){
//...
}())
//with params. if u ever wondered how that $ variable was being used in jquery, this is how.
(function(jquery){
})(jquery);
//es6
(
() => { ... }
)()
closure is a programming concept in which (otherwise) scoped-out variables are allowed to persist. DO NOT use closure with big object collections !!!
var outer = function(p1){
var inner = function (p2) {
return (p1 + p2);
return inner;
}; //outer ends.
var x = outer(10);
x(20); //20 + 10;
x(30); //30 + 10;
x(40); //40 + 10;
I thought making a simple function where if you click on a button a number will show up inside of a paragraph. And if you continue to click on the button the number inside the paragraph tag will increase. However, I'm getting an error message saying that getElementsByTagName is not a function. Here is the code on jsfiddle, I know there is something simple that I'm doing wrong but I don't know what it is.
HTML
<div class="resist" id="ex1"><h2>Sleep</h2><p></p><button>Resist</button></div>
<div class="resist" id="ex2"><h2>Eat</h2><p></p><button>Resist</button></div>
Javascript
var count = 0;
var resist = document.getElementsByClassName('resist') ;
for(var i = 0; i < resist.length; i++)
{ var a = resist[i];
a.querySelector('button').addEventListener('click', function(a){
count +=1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
}
You are overwriting a variable with event object passed into event handler. Change the name to e maybe, or remove it altogether as you are not using it anyway:
a.querySelector('button').addEventListener('click', function(e /* <--- this guy */) {
count += 1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
Another problem you are going to have is classical closure-in-loop issue. One of the solutions would be to use Array.prototype.forEach instead of for loop:
var count = 0;
var resist = Array.prototype.slice.call(document.getElementsByClassName('resist'));
// ES6: var resist = Array.from(document.getElementsByClassName('resist'));
resist.forEach(function(a) {
a.querySelector('button').addEventListener('click', function(e) {
count += 1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
});
vars in Javascript are function scoped, so you must wrap your event listener binding in a closure function to ensure the variable you're trying to update is correctly set.
(Note: I've renamed a to div in the outer function and removed the arg from the inner click function).
var count = 0;
var resist = document.getElementsByClassName('resist') ;
var div;
for(var i = 0; i < resist.length; i++)
{
div = resist[i];
(function(div){
div.querySelector('button').addEventListener('click', function(){
count +=1;
div.getElementsByTagName('p')[0].innerHTML = count;
});
})(div);
}
I created a function that would do something unique on it's 3rd call. In this case, alert the incremented number. However, when trying to call this function from another function:
"MyFunction();", I get Uncaught TypeError: Object [object global] has no method 'MyFunction'. Can you please tell me what I'm doing wrong?
var counter = 0;
var num = 0;
function = MyFunction() {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}
I've also tried removing the = sign, as you can see here http://jsfiddle.net/DN3yC/6/ it doesn't work.
LIVE DEMO
Just remove the = sign function MyFunction() { and make sure in your fiddle that JS <script> is in the right placeSee additional explanation below.
Example:
var element = document.getElementById('button');
var counter = 0;
var num = 0;
function MyFunction() {
counter = ++counter % 3; // loop count
if ( !counter ) {
num++;
alert('the new number is: ' + num);
}
}
//On click:
element.addEventListener('click', MyFunction, false);
your new fiddle: http://jsfiddle.net/DN3yC/7
The old one of yours was not working cause you did not used No Wrap - in body for the JS in your fiddle. (See options panel at the left up corner)
So basically you need to put your <script> tags before your </body> closing tag in order to make sure the DOM is ready and parsed by JavaScript before elements manipulation.
Syntacs looks wrong to me, try
var foo = Myfuntion();
or
var foo = new MyFuntion();
depending on what MyFuntion() is.
It should be:
var MyFunction=function () {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}