Function works more times, than needs - javascript

I have dynamically created elements. In vanilla JS we can communicate with such elements only with document.addEventListener('click',function(){//do smth});. So I have a script, which takes dynamically created buttons and adds event listener for them.
Problem is when I press some button one time, it's ok, but then script starts to count clicks and do code in btn.addEventListener('click', function(){ //do smth }); according to count of clicks. For example, I click 1 time - script makes 1 time, I click 2 times - script makes 3 times, I click 3 times - script makes 6 times, cause he counted all clicks. How can I resolve this problem?
document.addEventListener('click', addInCart);
function addInCart(){
const productBtns = document.querySelectorAll('.product__btn');
for(let btn of productBtns){
btn.addEventListener('click', function(){
//do smth
});
}
}

Because each time you click on the document will recall the function again, add {once: true} to avoid that.
document.addEventListener('click', addInCart, {once: true});
function addInCart(){
const productBtns = document.querySelectorAll('.product__btn');
for(let btn of productBtns){
btn.addEventListener('click', function(){
//do smth
});
}
}

use .onclick = function() { ... } instead of .addEventListener('click', function() { ... })
document.addEventListener('click', addInCart);
function addInCart(){
const productBtns = document.querySelectorAll('.product__btn');
for(let btn of productBtns){
btn.onclick = function(){
//do smth
};
}
}

Related

How to make a button in JavaScript execute code only once?

I created a button that creates a grid. However, clicking on the button repeatedly executes the code repeatedly and I only want it to execute once. The code is below:
inputBtn.addEventListener("click", function () {
createGrid();
});
function createGrid() {
for (let i = 0; i < 256; i++) {
div = document.createElement("div");
div.classList.add("grid-child");
if (gridContainer) {
randomColor();
}
gridContainer.appendChild(div);
}
}
I created a loop that I thought would stop executing after 256 times. I also tried adding a break at the very end but that led to some other problems.
Set the once option to true when adding the listener:
inputBtn.addEventListener(
"click",
function () {
createGrid();
},
{ once: true }
);

Module overwrite previous attached window mouseup event

I am currently working on a javascript module which open and close boxes, tooltip or similar, the function works great the only problem is when I call it twice on a page where the 'boxes' classes are different the window mouseup event will be overwritten and only one of the two module instances of boxes can now be closed after opening them.
var boxRevealer = (function () {
var buttons;
var boxes;
var element;
var drp_active = false;
var boxConstruct = function (btns, bxs) {
buttons = document.querySelectorAll(btns);
boxes = document.querySelectorAll(bxs);
boxEvents();
};
var boxEvents = function () {
buttons.forEach(function (e) {
e.addEventListener("click", function (ee) {
element = document.getElementById(e.getAttribute("data-drp"));
element.classList.toggle("displayn");
drp_active = true;
});
});
window.addEventListener("mouseup", function (e) {
if (drp_active === true) {
if (!e.target.classList.contains("filt_holy")) {
boxes.forEach(function (e) {
console.log("ELEMENT");
console.log(e);
e.classList.add("displayn");
});
}
}
}, false);
};
return {
boxConstruct: boxConstruct,
boxEvents: boxEvents
};
})();
Here is how i call the module
window.addEventListener("load", function(e){
boxRevealer.boxConstruct(".head_drp_btn", ".head_drp");
boxRevealer.boxConstruct(".mkt_drp_btn", ".mkt_drp");
});
So my question is, should I always name the boxes the same, or is there a work around?
Just remove the event before adding it, I think the same event is getting called twice.
So updated code will be as follows:
// Attach an event handler to <div>
e.addEventListener("mousemove", myFunction);
// Remove the event handler from <div>
e.removeEventListener("mousemove", myFunction);
And remove the window event as well before adding it.

JavaScript on body click work 1 time

This is my script -
my script alert when someone touch on any place on the page .
I am trying to execute alert only one time and not on every click.
This is the code i built which alert any time .
$( document ).ready(function() {
var click_count = 0;
if (click_count == 0){
$('body').click(function(){
alert();
var click_count = 1;
});
}
});
You have your if in the wrong place. You want it inside the click handler (the code that runs when the click occurs), not outside it. You also need to remove the var from var click_count = 1; so you 're using the one declared in the ready callback, not the one declared in the click handler:
$(document).ready(function() {
var click_count = 0;
$('body').on("click", function() {
if (click_count == 0) {
alert("Hi there");
click_count = 1;
}
});
});
Testing 1 2 3
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
(Here's a runnable version for mobiles where Stack Snippets aren't rendered: http://output.jsbin.com/taropelopu)
But, rather than using click_count, I'd suggest removing the event handler after the first click. You could do that with off, but jQuery even has a function specifically for adding a handler you remove the first time it's called: one (rather than on):
$(document).ready(function() {
$('body').one("click", function() {
alert("Hi there");
});
});
Testing 1 2 3
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
(Runnable version for devices where Stack Snippets don't render: http://output.jsbin.com/wivoroluzu)
With plain JavaScript you can do
let clicked = false;
const clickOnce = () => {
if (!clicked) {
alert('clicked');
clicked = true;
}
};
document.body.addEventListener('click', clickOnce);
some body content
And you don't even need a clicked variable to store the state. Just remove the event listener once it is triggered.
const clickOnce = () => {
alert('clicked');
document.body.removeEventListener('click', clickOnce)
};
document.body.addEventListener('click', clickOnce);
some body content

removeEventListener doesn't seems to work?

for some reason ,I need to delay click and preventDefault for some time when scroll page end.So I write something like this :
// const disableClickDuringScrollHandler=(e)=> {
// e.preventDefault();
// };
this.scrollHandler = e => {
console.log('scrolling');
const disableClickDuringScrollHandler = (e) => {
e.preventDefault();
};
document.addEventListener('click', disableClickDuringScrollHandler);
window.clearTimeout(this.scrollTimer);
this.scrollTimer = window.setTimeout(() => {
console.log('scroll end');
document.removeEventListener('click', disableClickDuringScrollHandler);
}, 300);
}
window.addEventListener('scroll', this.scrollHandler);
I also has been write a codepen: https://codepen.io/zhangolve/pen/gRNMoX
my question is when I put disableClickDuringScrollHandler outside of the scrollHandler ,removeEventListener can work well,but when I put disableClickDuringScrollHandler inside of the scrollHandler ,removeEventListener doesn't seem to work .
I have tried so many times to found why but failed.So I get here to ask your help.
The problem is that each time the user scrolls, you create a new disableClicksDuringScroll closure and add it as a click listener. When this timer runs, it removes the latest click listener, but not the previous ones (because they're different closures, so they aren't equal to the function you're removing this time).
You should define disableClicksDuringScroll function just once, outside the scroll handler, since it doesn't refer to any local variables here. Then when you call removeEventListener it will find this handler.
You can also use a variable so you only add the click listener once, when scrolling starts, not every time you reset the timer.
this.disableClickDuringScrollHandler = (e) => {
e.preventDefault();
};
this.inScroll = false;
this.scrollHandler = e => {
console.log('scrolling');
if (!this.inScroll) {
document.addEventListener('click', this.disableClickDuringScrollHandler);
this.inScroll = true;
}
window.clearTimeout(this.scrollTimer);
this.scrollTimer = window.setTimeout(() => {
this.inScroll = false;
console.log('scroll end');
document.removeEventListener('click', disableClickDuringScrollHandler);
}, 300);
}
window.addEventListener('scroll', this.scrollHandler);

Event-bubbling of a draw() function in JS

Following code snippet to show the core structure:
EDIT1:
var smthing = 0;
change_name_button.addEventListener('click', function(event){
// some query
// .....
// result from query
data = res.name.coords;
// tried to make the function go to GC
smthing = null;
delete smthing;
smthing = new drawMouseMovement(data);
event.stopPropagation();
}, false);
function drawMouseMovement(data){
// bunch of variables set for drawing function
// .....
// .....
var test = 0;
// draw something to canvas until end "maxLength" is reached
var draw = function() {
if(t < maxLength){
// redraw some stuff to canvas
// .....
}else{
return;
}
}
// function to play pause and replay the drawing on canvas
function playPause(boolPP, boolRP){
if(boolPP){
test = setInterval(draw, 10);
}else{
clearInterval(test);
}
if(boolRP){
// some params being reset
// .....
test = setInterval(draw, 10);
}
}
function play(event){
playPause(true, false);
event.stopPropagation();
}
function pause(event){
playPause(false, false);
event.stopPropagation();
}
function replay(event){
playPause(false, true);
event.stopPropagation();
}
play_button.addEventListener('click', play, false);
pause_button.addEventListener('click', pause, false);
replay_button.addEventListener('click', replay, false);
}
Everytime the change_name_button is clicked drawMouseMovement() is called with new parameters.
Following problem: When the draw function does not reach return before clicking change_name_button again, there are two instances of the same function drawing two different things at the same time. Only the last called instance of the function should be drawing.
I tried deleting the pointer to the function, clearInterval() and removeEventListener. But I don't seem to get any of those to work.
I hope my problem is clear.
Thanks in advance.
EDIT
A simple replication of the problem. Is some_button clicked once, smt is printed every 250ms. Is some_button clicked a second time, smt is printed every 125ms etc. How do I overwrite the first instance of printer() with the next click?
some_button.addEventListener('click', foo, false);
function foo(event) {
printer();
event.stopPropagation();
}
function printer(){
setInterval(function() {
console.log("smt");
}, 250);
}
UPDATE 2
A simple replication of the problem. Is some_button clicked once, smt is printed every 250ms. Is some_button clicked a second time, smt is printed every 125ms etc. How do I overwrite the first instance of printer() with the next click?
No way! Don't kill the function, let it ride. First you need a counter to count the clicks so the function knows it's been clicked the second time. Second, just add extra behavior for when the button is clicked the second time, in this case, you are going to half the time interval.
Ok, when I said JavaScript time is quircky, I meant fu##3d. setInterval needs clearInterval to stop it. What most articles and tutorials fail to say is that the damn setInterval still exists, so making it null will allow you to create a new one. I decided to make a constructor timer.
The demo has a button, click it and it'll print out 'smt' every 2 seconds plus the number of clicks to start a new interval.
The 'Go' button is replaced by a 'Stop' button, once clicked it does as advertised--stops
Click it again and it's the 'Go' button, but this time, it is printing every second now.
Lather, rinse, repeat.
Refactor this demo with some prototype wizardry and you got yourself a class.
PLUNKER - TIMER CONSTRUCTOR
PLUNKER - 1 EVENTLISTENER, MULTIPLE EVENT.TARGETS
SNIPPET
<!DOCTYPE html>
<html>
<head>
<style>
.on { display: block; }
button { display: none; }
</style>
</head>
<body>
<button id="btn1" class="on">Go</button><button id="btn2" class="">Stop</button>
<script>
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var counter = 0;
var timer = new Timer();
btn1.addEventListener('click', xStart, false);
btn2.addEventListener('click', xStop, false);
function xStart(event) {
timer.term;
counter++;
timer.start(counter);
btn1.classList.toggle('on');
btn2.classList.toggle('on');
event.stopPropagation();
}
function xStop(event) {
timer.term();
btn1.classList.toggle('on');
btn2.classList.toggle('on');
event.stopPropagation();
}
function Timer(c){
var self = this;
self.start = function(c){
var t = self.printer(c);
self.interval = setInterval(function() { self.printer(c); },t);
};
self.term = function(){
self.clear = clearInterval(self.interval);
self.null;
};
self.printer = function(x){
var d = (x % 2 === 0) ? 2 : 1;
var t = 2000 / d;
console.log('smt'+x+' t: '+t);
return t;
};
}
</script>
</body>
</html>
UPDATE 1
The following Plunker demonstrates how you can make the only eventListener the element that your buttons all share as the parent.
PLUNKER
OLD
None of your eventListeners() have anything for capturing phase, although I believe it's false by default, try setting it to false anyways.
change_name_button.addEventListener('click', function(event){
// some query
// .....
// result from query
data = res.name.coords;
// tried to make the function go to GC
smthing = null;
delete smthing;
smthing = new drawMouseMovement(data);
event.stopPropagation();
}, false);
^^^^^^^^====== There is the capturing phase`
Use event.stopPropagation(); on the other eventhandlers as well and place close to the end of handler. Don't forget to pass the (event) object.
function play(event){
playPause(true, false);
event.stopPropagation();
}
function pause(event){
playPause(false, false);
event.stopPropagation();
}
function replay(event){
playPause(false, true);
event.stopPropagation();
}
play_button.addEventListener('click', play, false);
pause_button.addEventListener('click', pause, false);
replay_button.addEventListener('click', replay, false);
Also, if that doesn't fix the issue then you need to post the HTML because the layout of your event.target and event.currentTarget might have to be adjusted.
EDIT
Try changing the if else
function drawMouseMovement(data){
// bunch of variables set for drawing function
// .....
// .....
var test = 0;
// draw something to canvas until end "maxLength" is reached
var draw = function() {
if(t < maxLength){
// redraw some stuff to canvas
// .....
}
// when t has reached maxlength, then it should quit?
return false;
}

Categories