A click event seems firing a function call several times - javascript

I want to make a simple game, learning HTML, CSS and javaScript. I wrote code in which I want a function to go to the next one by clicking a button. Its has a flow like the code below. It does not work as I intend. I want it to return to the first function when a round finished. But from the second round buttons start to call next funcitons several times by one click. What do I misunderstand? I show the console output at the bottom.
<body>
<button id="toFunc2">toFunc2</button>
<button id="toFunc3">toFunc3</button>
<button id ="toFunc4">toFunc4</button>
<button id ="toFunc1">toFunc1</button>
<p></p>
</body>
<script>
window.addEventListener("DOMContentLoaded", function(){
function functionFour(obj) {
obj.nowFunctionFour += 1;
console.log(`nowFunctionFour: ${ obj.nowFunctionFour }`);
obj.toFunc1Button.addEventListener("click", function(){
functionOne(obj);
})
}
function functionThree(obj) {
obj.nowFunctionThree += 1;
console.log(`nowFunctionThree: ${ obj.nowFunctionThree }`);
obj.toFunc4Button.addEventListener("click", function(){
functionFour(obj);
});
}
function functionTwo(obj) {
obj.nowFunctionTwo += 1;
console.log(`nowFunctionTwo: ${ obj.nowFunctionTwo }`);
obj.toFunc3Button.addEventListener("click", function(){
functionThree(obj);
});
}
function functionOne(obj) {
obj.nowFunctionOne += 1;
console.log(`nowFunctionOne: ${ obj.nowFunctionOne }`);
obj.toFunc2Button.addEventListener("click", function(){
functionTwo(obj);
})
}
const obj = {
toFunc2Button: document.getElementById("toFunc2"),
toFunc3Button: document.getElementById("toFunc3"),
toFunc4Button: document.getElementById("toFunc4"),
toFunc1Button: document.getElementById("toFunc1"),
nowFunctionOne: 0,
nowFunctionTwo: 0,
nowFunctionThree: 0,
nowFunctionFour: 0
}
functionOne(obj);
})
</script>
-------Console Output-------
nowFunctionOne: 1
nowFunctionTwo: 1
nowFunctionThree: 1
nowFunctionFour: 1
nowFunctionOne: 2
nowFunctionTwo: 2
nowFunctionTwo: 3

The way the event listeners work in JavaScript is that:
You can attach multiple event listeners to the same DOM element
Because of this, you can accidentally attach the same listener twice or more times to the same element. That way, for each event you will see that the event listener is being invoked multiple times although they are actually different listeners with the same code! This is what has happened with your implementation.
On your first round, when functionOne is invoked, an event listener gets attached. On your second round, functionOne is invoked again & by executing the code line by line another event listener gets attached (with same code) to the element. Hence you get the issue!
I would propose you attach the event handlers separately & for one time only.
Solution
<html>
<body>
<button id="toFunc2">toFunc2</button>
<button id="toFunc3">toFunc3</button>
<button id="toFunc4">toFunc4</button>
<button id="toFunc1">toFunc1</button>
<p></p>
<script>
window.addEventListener("DOMContentLoaded", function () {
const obj = {
nowFunctionOne: 0,
nowFunctionTwo: 0,
nowFunctionThree: 0,
nowFunctionFour: 0,
};
function functionFour() {
obj.nowFunctionFour += 1;
console.log(`nowFunctionFour: ${obj.nowFunctionFour}`);
}
function functionThree() {
obj.nowFunctionThree += 1;
console.log(`nowFunctionThree: ${obj.nowFunctionThree}`);
}
function functionTwo() {
obj.nowFunctionTwo += 1;
console.log(`nowFunctionTwo: ${obj.nowFunctionTwo}`);
}
function functionOne() {
obj.nowFunctionOne += 1;
console.log(`nowFunctionOne: ${obj.nowFunctionOne}`);
}
const attachEventListeners = () => {
document.getElementById("toFunc1").addEventListener("click", functionOne);
document.getElementById("toFunc2").addEventListener("click", functionTwo);
document.getElementById("toFunc3").addEventListener("click", functionThree);
document.getElementById("toFunc4").addEventListener("click", functionFour);
};
attachEventListeners();
})
</script>
</body>
</html>

Related

Event executes functions couple times in recursion

I have similar problem in my main project. Down below I shown my problem to the simplified way. I know why my code in executing couple times, the question is how to repair this.
HTML Code:
<body>
<button id="btn">CLICK ME</button>
<script src="index.js" defer></script>
</body>
Javascript:
const btn = document.getElementById("btn");
var number = 1;
function load() {
console.log(number);
btn.addEventListener('click', (e) => add(e))
};
function add(e) {
number += 1;
load();
e.stopPropation();
}
load();
And of course when I click the button first time everything is fine, but when I click second time, the button executes function two times, after third click four times and so on. I thought that e.stopPropation(); will solve the problem, but unfortunately is not.
Main Question:
How to kill events which are doubled?
here use this
const btn = document.getElementById("btn");
var number = 1;
function load() {
console.log(number);
btn.addEventListener("click", (e) => add(e));
}
function add(e) {
number += 1;
load();
e.stopImmediatePropagation();
}
load();

JavaScript event target

I have problem with event target. I want to check which element were clicked but it don't work. What is the solution this problem ?
var prod = document.querySelectorAll("button");
function dodaj(e) {
var tar = e.target;
console.log(tar);
}
for(var i = 0; i < prod.length; i++) {
prod[i].addEventListener("click", function() {
dodaj();
}, false);
}
You haven't passed anything into dodaj. You could just pass dodaj directly into addEventListener.
prod[i].addEventListener("click", dodaj, false);
The issue is that your click event handler was an anonymous function that was putting in a call to the actual function that does the work (dodaj) and that function was not receiving the reference to the event - the anonymous function was.
You can change the anonymous function so that it receives the event and then passes it to dodaj like this:
prod[i].addEventListener("click", function(evt) {
dodaj(evt);
}, false);
However, since that "wrapper" function really wasn't adding any value to your code, you can/should remove it entirely and just register the actual callback function as the click event handler.
var prod = document.querySelectorAll("button");
function dodaj(e) {
console.log(e.target);
}
for(var i = 0; i < prod.length; i++) {
// When any button gets clicked, call the dodaj function
// directly. This is the function that will receive a
// reference to the click event.
prod[i].addEventListener("click", dodaj);
}
<button id="one">Click Me</button>
<button id="two">Click Me</button>
<button id="three">Click Me</button>
<button id="four">Click Me</button>

Toggle Event Listeners

I am trying to make a function that would allow me to toggle eventListener of an element.
In the example below, I have three buttons: main, on and off. When I click on the on button, the main button becomes functional. After I click off button, the main button should not work anymore (but now it still does).
Now I can achieve a desired behavior by clicking on button for the second time, but I guess it's a bad coincidence and it's not supposed to work that way.
Maybe I should add that I would like to work this out without using jQuery or similar and it needs to be a function, because I am going to use it for a lot of buttons.
(I suspect something with scope causes the problem (clickHandler when calling the function to activate the button is not the same as the clickHandler when calling the function to disable the button), but I can't think of a way to test it.)
// buttons definitions, not important
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButton = document.querySelector("#offButton");
// main function
var toggleButtons = function(toggleVal, button, element) {
var activateButton, clickHandler, disableButton;
// callback function for listener bellow
clickHandler = function() {
document.querySelector(element).classList.toggle("yellow");
};
activateButton = function() {
button.addEventListener("click", clickHandler);
};
disableButton = function() {
button.removeEventListener("click", clickHandler);
};
// when first argument is 1, make the button functional, otherwise disable its functionality
if (toggleVal === 1) {
activateButton();
} else {
disableButton();
}
};
// when onButton is clicked, call main function with arguments
// this works
onButton.addEventListener("click", function() {
toggleButtons(1, mainButton, "body");
});
// this fails to disable the button
offButton.addEventListener("click", function() {
toggleButtons(0, mainButton);
});
.yellow {
background-color: yellow;
}
<button type="button" id="mainButton">mainButton
</button>
<button type="button" id="onButton">onButton
</button>
<button type="button" id="offButton">offButton
</button>
<p>mainButton: toggles background color on click
</p>
<p>onButton: turns on mainButtons's functionality</p>
<p>offButton: supposed to turn off mainButton's functionality</p>
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButon = document.querySelector("#offButton");
var element; // declare the element here and change it from toggleButtons when needed.
function clickHandler() {
document.querySelector(element).classList.toggle('yellow');
}
function activateButton(button) { // You missed this part
button.addEventListener('click', clickHandler);
}
function disableButton(button) { // You missed this part
button.removeEventListener('click', clickHandler);
}
function toggleButtons(value, button) {
if (value === 1) {
activateButton(button); // You missed this part
} else {
disableButton(button); // You missed this part
}
};
onButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(1, mainButton);
});
offButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(0, mainButton);
});
Below code helps to toggle between two functions from an eventListener:
var playmusic=false;
function playSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.currentTime = 0
audio.play()
playmusic=true;
}
function stopSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.pause()
playmusic=false;
}
window.addEventListener('keydown',
function(){playmusic?stopSound():playSound()} )

Can't remove event listener

Can anyone tell why bt2 event listener is not getting removed in if block. As when I remove the event listener in the p function, it's getting removed without any error or bug. I am pretty sure there might be any stack or scope problem due to which event listener is not getting removed but I can't figure out what that could be. And I know that event listener is not getting removed as with the succeeding clicks on bt2 element all the preceding event listeners are also running again, as the same function is running multiple times. Please tell me what's the problem.
Here's the full code:
(function()
{
if(window.addEventListener) window.addEventListener('load',init,false);
function init()
{ var i=0;
var get=document.getElementById('bt1').addEventListener('click',function() { pro(0);},false);
function pro(x)
{ alert('yeah');
if(!x) x=0
if(x!=0) //I dont want to remove event listener in the first time , so i want it to function with the next call to pro,in which the value of x is bigger than 0
{
//alert('right');
document.getElementById('bt2').removeEventListener('click',p,false); //event listener is not getting removed .
}
document.getElementById('bt2').innerHTML='this is a button '+x;
function passTo(y)
{
pro(y);
}
document.getElementById('bt2').addEventListener('click',p,false);
function p()
{ //document.getElementById('bt2').removeEventListener('click',p,false); //here the event listener is getting removed easily
passTo(x+1);
}
}
}
}());
removeEventListener requires that you pass it the same function, but your p functions are not the same: A new one is created every time pro is called. So the one you're trying to remove isn't the one you added, and so it isn't removed.
Removing it within p works, because within each p function, the identifier p refers to that specific p function. So if that one's been added, it will successfully remove itself.
You can prove this to yourself by putting a unique identifier on your function (see comments):
(function() {
if (window.addEventListener) window.addEventListener('load', init, false);
var functionId = 0; // Something to give us unique IDs
function init() {
var i = 0;
var get = document.getElementById('bt1').addEventListener('click', function() {
pro(0);
}, false);
function pro(x) {
snippet.log('yeah');
// We ALWAYS to into the body of this if, the condition
// is just here for emphasis
if (!p.id) {
p.id = ++functionId;
}
if (!x) x = 0
if (x != 0)
{
snippet.log("Removing #" + p.id); // <===
document.getElementById('bt2').removeEventListener('click', p, false);
}
document.getElementById('bt2').innerHTML = 'this is a button ' + x;
function passTo(y) {
pro(y);
}
snippet.log("Adding #" + p.id); // <===
document.getElementById('bt2').addEventListener('click', p, false);
function p() {
passTo(x + 1);
}
}
}
}());
<button id="bt1">bt1</button>
<button id="bt2">bt2</button>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
If we run that and click bt1 once, then repeatedly click bt2, we see:
yeah
Adding #1
yeah
Removing #2
Adding #2
yeah
Removing #3
Adding #3
yeah
Removing #4
Adding #4
Note how each time we're trying to remove a different function than we added.
If you want to remove the previous one, you need to remember it elsewhere (see comments):
(function() {
if (window.addEventListener) window.addEventListener('load', init, false);
var functionID = 0;
var lastp = null; // <===
function init() {
var i = 0;
var get = document.getElementById('bt1').addEventListener('click', function() {
pro(0);
}, false);
function pro(x) {
snippet.log('yeah');
if (!p.id) { // Again, always true
p.id = ++functionID;
}
if (!x) x = 0;
if (lastp) // <===
{
snippet.log("Removing #" + lastp.id);
document.getElementById('bt2').removeEventListener('click', lastp, false);
}
document.getElementById('bt2').innerHTML = 'this is a button ' + x;
function passTo(y) {
pro(y);
}
lastp = p; // <===
snippet.log("Adding #" + p.id);
document.getElementById('bt2').addEventListener('click', p, false);
function p() {
passTo(x + 1);
}
}
}
}());
<button id="bt1">bt1</button>
<button id="bt2">bt2</button>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Stop function after certain number of clicks on a certain element

OK so I am making a reaction tester, and I have a function that makes shapes appear on screen, So what I want is some sort of function were after 5 clicks on a certain element it will end a function. Is there a way of doing that? sorry if its a dumb question, its because I am new to the whole coding...
Here you go
var clickHandler = (function (e) {
var count = 0;
return function () {
count += 1;
if (count > 5) {
return;
}
// do other stuff here
}
}());
aDiv.addEventListener('click', clickHandler, false);
You Can use static variable to count how many times the object has been clicked.
and here is how you can create static variable in javascript.
You can unbind the click event once the counter reaches 5. See the example below
function test(sender) {
sender.dataset.clicked++;
console.log("I've been clicked", sender.dataset.clicked);
if (+sender.dataset.clicked === 5) {
// unbind the event
sender.onclick = null;
}
return;
}
<div onclick="test(this);" data-clicked="0">click me</div>
You may use global variable which may remain counting on click function
<script>
var globalvar = 0;
onclickfunct()
{
globalvar += 1;
if(globalvar == 5)
{
//do my work
}
else
{
//give alert
}
}
</script>

Categories

Resources