function rightAnswer() {
console.log('right')
var countRight = 0;
[Button2, Buttonb3].forEach(a =>
a.addEventListener('click', () => {
countRight += 1;
})
);
}
I need help with being able to print consol.log(countRight). So far when i use that line of code the consol always shows countRight = 0 even though it should say either 1 or 2 depending on the users input. I need help with making this code work.
It does not work if i put consol.log(countRight) after countRight += 1;
function rightAnswer() {
var countRight = 0;
[Button2, Buttonb3].forEach(a =>
a.addEventListener('click', () => {
countRight += 1;
// this is where you should be able to log the value
console.log(`right value is: ${countRight}`);
})
);
}
This is spaghetti code though. Your function breaks scope by referencing "Button2", "Buttonb3"
If you want to log on clicks the right place to log is in the click handler.
The implication from naming is that countRight will be called to increment the count–as it is no handlers will be added until countRight has been called (and it should be called only once so multiple handlers aren't added).
It might make more sense to add the handlers on the load event. If you really mean to add them at an arbitrary time in the future it can certainly be put in a method, noting again it should only be called once, or the event listeners removed before calling it again.
window.addEventListener('load', () => {
let countRight = 0
const button1 = document.getElementById('button1')
const button2 = document.getElementById('button2')
const buttons = [button1, button2]
const output = document.getElementById('output')
buttons.forEach(b => {
b.addEventListener('click', b => {
countRight++
output.innerText = countRight
console.log(`countRight = ${countRight}`)
})
})
}, false);
<button id="button1">Button #1</button>
<button id="button2">Button #2</button>
<div>Clicked <span id="output">0</span> times.</div>
Tangential
The reason to post a complete example is so people can copy it into their answers and actually run it. It also makes sense to
Related
So I'm developing a webpage and I want to display the value of a variable in a part of it. The variable is being manipulated by many functions and I want the displayed value as the current value of the variable. How exactly do I do that? By the way here's the code. Even if the functions manipulate the variable, it still shows "1"
var cpc = 1;
document.getElementById("curcpc").innerHTML = cpc;
There's nothing that will automatically update the DOM for you when the variable changes. (There are lots of libraries or frameworks that will update the DOM for you if you follow their rules for how you change it, but nothing that will do it completely automatically.)
There are a few ways to do it.
Update function / accessor
One simple way to fix that is to not make it a simple variable. Instead, make setting it something that requires a function call, and have the function call update both the variable and the DOM.
You might make updating it an explicit function call (explicit is usually good!), or you could make it an accessor property with a getter and setter. The upside is you're not going to miss any updates. The downside is it's a function call every time you get or set the "variable"'s value (though with an accessor it doesn't look like one).
Here's a really basic version of that using an accessor:
const data = (() => {
let cpc;
const curcpc = document.getElementById("curcpc");
return {
get cpc() {
return cpc; // Returns the value of the variable
// declared above
},
set cpc(newValue) {
cpc = newValue; // Updates the var above
curcpc.innerHTML = cpc;
}
};
})();
data.cpc = 0;
// Some place updating it
document.getElementById("btn-add").addEventListener("click", () => {
++data.cpc;
});
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", () => {
--data.cpc;
});
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">
But here's a version that's more efficient, not updating the display until the next time the browser is about to paint the page (by asking for a callback via requestAnimationFrame), which means repeated updates that wouldn't have been visible don't get made unnecessarily:
const data = (() => {
let cpc;
const curcpc = document.getElementById("curcpc");
let rAFHandle = 0;
const updateCurCpc = () => {
curcpc.innerHTML = cpc;
rAFHandle = 0;
};
return {
get cpc() {
return cpc; // Returns the value of the variable
// declared above
},
set cpc(newValue) {
cpc = newValue;
if (!rAFHandle) {
rAFHandle = requestAnimationFrame(updateCurCpc);
}
}
};
})();
data.cpc = 0;
// Some place updating it
document.getElementById("btn-add").addEventListener("click", () => {
++data.cpc;
});
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", () => {
--data.cpc;
});
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">
Wrap handlers
Another way is to wrap any event handlers and such in a function that remembers the old value and updates the DOM after the event handler is done if the value has changed. This has the advantage that reading/writing the variable is a simple read/write, not a function call, but adds the cost of doing the comparison after each event handler completes and the possibility of adding a hanlder you forget to wrap.
Here's a basic example of that (without requestAnimationFrame, it doesn't add much in this case):
let cpc = 0;
const curcpc = document.getElementById("curcpc");
curcpc.innerHTML = cpc;
const wrapHandler = (handler) => {
// Return a non-arrow function (we want `this` set by the caller)
return function(...args) {
try {
const result = handler.apply(this, args);
curcpc.innerHTML = cpc;
return result;
} catch (e) {
curcpc.innerHTML = cpc;
throw e;
}
};
};
// Some place updating it
document.getElementById("btn-add").addEventListener("click", wrapHandler(() => {
++cpc;
}));
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", wrapHandler(() => {
--cpc;
}));
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">
var cpc = 1;
function updateValue(destination, newValue) {
document.getElementById(destination).innerHTML = newValue;
}
Then you can trigger this update from anywhere you'd like with whatever value you'd like. e.g. usage:
updateValue('curcpc', cpc);
Why is this code indicating that my object's properties are undefined?
document.getElementById("mkObj").addEventListener('click', () => {
var globalDragon = mkObj(document.getElementById("cn").value, parseInt(document.getElementById("ch").value), document.getElementById("cl").value);
document.getElementById("hit").addEventListener('click', (globalDragon) => {
hit(globalDragon, 25);
printObjDetails(globalDragon);
});
document.getElementById("details").addEventListener('click', (globalDragon) => {
let dragon_here = globalDragon;
printObjDetails(dragon_here);
});
});
Why can't I just make the Dragon object by clicking the mkObj button and then click the hit button or details button and have it show the object's state? Why is it showing that globalDragon.name is undefined?
I had the code looking like this before and it did not work either so I just make everything be within the mkObj click scope, thinking that that would solve the issue but it didn't:
document.getElementById("mkObj").addEventListener('click', ()=>{
var globalDragon = mkObj( document.getElementById("cn").value, parseInt( document.getElementById("ch").value), document.getElementById("cl").value );
});
document.getElementById("hit").addEventListener('click', (globalDragon)=>{
hit(globalDragon, 25);
printObjDetails(globalDragon);
});
document.getElementById("details").addEventListener('click', (globalDragon)=>{
let dragon_here = globalDragon;
printObjDetails(dragon_here);
});
I believe what you actually want is
// declare the variable in the top scope,
// so that all three click handlers can access it
var globalDragon;
document.getElementById("mkObj").addEventListener('click', () => {
// write a new object into the global variable
globalDragon = mkObj(
document.getElementById("cn").value,
parseInt( document.getElementById("ch").value),
document.getElementById("cl").value
);
});
document.getElementById("details").addEventListener('click', () => {
// read from the global variable
printObjDetails(globalDragon);
});
document.getElementById("hit").addEventListener('click', () => {
// modify the object
hit(globalDragon, 25);
printObjDetails(globalDragon);
});
Notice how I removed the parameters of the functions that were also named globalDragon. The event handlers get passed an event object as their arguments.
Is it possible to return the result of a function if it is called by an event listener? I have tried lots of ways but nothing works. To my surprise there is not much information regarding it that can help me out. I would like to pass in the current score , increment it by one when an event occurs and return. Obviously what i have done below will not work because a) there is no storage for the return statement and b) it is called by an event listener. The only thing i am resorting to doing is changing my global variable inside the function. I know that wouldnt be as harmful if wrapped in an IIFE or its own closure but i still dont want to get in the habit of doing it. Is there anyway of incrementing the score by 1 and not referencing the global score variable inside the function directly, thanks all
const btn = document.querySelector('#btn');
let score = 0;
btn.addEventListener('click', function () {
increaseScore(score); // passing in the current score varialble
})
function increaseScore(currentScore) {
currentScore++;
return currentScore; // there is nowhere to return this!!
}
The trick is you need to bind the context(this)
someMethod.bind(this);
const btn = document.querySelector('#btn');
const scoreBtn = document.querySelector('#score');
let score = 0;
btn.addEventListener('click', function() {
event.preventDefault();
someMethod.bind(this);
score = someMethod(score);
});
scoreBtn.addEventListener('click', function() {
event.preventDefault();
console.log(score);
});
function someMethod(cur) {
return cur + 1;
}
<button id="btn">Update Score</button>
<button id="score">Show Score</button>
I apologize if that title is confusing, so let me explain. I'm creating a Hangman Game to improve my HTML/CSS/Javascript skills. The HTML/CSS is all complete and functions, but the issue is with my javascript.
Basically my javascript works like this:
A user clicks the start button, the game function begins. Once the game begins, the user can type a letter and click 'submit.' The user's input should update the global variable, userInput but it does not. If my understanding is right (from looking around stack overflow unsure of the exact phrasing of my problem), this is something that cannot be done because of the async and scope nature of javascript. Is this correct? If not, is there any advice I can get on making it work?
let userInput = '';
function getValue(e) {
e.preventDefault();
userInput = document.getElementById('inputText').value.toLowerCase();
form.addEventListener('click', form.reset());
//console.log(userInput); <-- works
}
function hangMan(e) {
submit.addEventListener('click', getValue);
const word = getWords(words);
const MAX_ATTEMPTS = word.length + 3;
console.log(userInput) <-- does not
let attempt = 0;
let win = false;
dashedWord = getWordBlanks(word);
wordBlanks.innerHTML = dashedWord;
while (attempt <= MAX_ATTEMPTS && win === false) {
let checkedWord = checkAndReplaceCharacter(word, dashedWord, userInput);
if (checkedWord === word) {
win = true;
} else {
wordBlanks.innerHTML = checkedWord;
}
attempt += 1;
}
}
function main() {
startButton.addEventListener('click', hangMan);
}
main();
The async and scope seem to be fine.
You can change a global variable from inside an event listener.
console.log(userInput) <-- does not displays an empty string because at the time it executes userInput still holds the default value let userInput = ''; i.e. the whole hangMan(e) method is executed before any click on the submit button so the listener is not yet executed.
Keep in mind, though. If you actually allow the form to be submited, you reload the papge and loose all variables.
I have created the following click count factory for adding the click count to the event information already present in a regular click event. This function creates a clickCountObj to track the number of clicks, as well as a new function for catching a click event on the given element parameter and reporting it back to the listener parameter along with the click count.
Originally, I wanted to do this as a class, rather than a factory... Way back when I was working in Java, I would have done it with a façade class, so that's what I was thinking. But I've concluded that it is not possible in Javascript, because the same function you'd use to create the object would be the one called in response to the click, and I can't see a way around this.
The purpose of this question is simply to improve my understanding and using of JavaScript. Please let me know if I am wrong in my conclusion stated above, or if there are any other alternatives to doing this a better way?
function clickCount(element, listener) {
let clickCountObj = {};
clickCountObj.clickCount = 0;
clickCountObj.clickDelay = 500;
clickCountObj.element = element;
clickCountObj.lastClickTime = 0;
let clickCountListener = function (e) {
// alert("last click time: " + clickCountObj.clickDelay);
if ((e.timeStamp - clickCountObj.clickDelay) < clickCountObj.lastClickTime) {
clickCountObj.clickCount = clickCountObj.clickCount + 1;
// alert("click count up: " + clickCountObj.clickCount);
}
else {
clickCountObj.clickCount = 1;
}
clickCountObj.lastClickTime = e.timeStamp;
listener.call(element, clickCountObj.clickCount, e);
};
if (!element) throw "No element to listener to";
element.addEventListener("click", clickCountListener);
return clickCountListener;
}
For sure you can also use a class:
class ClickCounter {
constructor(element, onClick, delay = 500) {
this.element = element;
this.onClick = onClick;
this.counter = 0;
this.delay = delay;
this.lastClicked = 0;
element.addEventListener("click", () => this.click(), false);
}
click() {
if(Date.now() < this.lastClicked + this.delay)
return;
this.lastClicked = Date.now();
this.onClick.call(this.element, this.counter++);
}
}
new ClickCounter(document.body, count => {
alert(count);
});
[is] doing this a better way?
No, not really. Using a class is not really useful here as you don't want to expose properties and you also don't need inheritance. A factory seems to be a good approach here.
Small sidenote: Instead of
return clickCountListener;
it would make more sense to
return clickCountObj;
as it would expose the settings and the count which might be useful.
warning: unserious content below
Way back when I was working in Java ...
... you took over that senseless naming scheme (clickCountObj.clickCount). I guess you won't loose any necessary information with just settings.count ...