Martin
Martin

Reputation: 79

How to stop effect of click during setTimeout?

I have a simple function which change opacity of divs every x seconds. When I click on "pause button", this one make a pause in this loop, and get the color of the current div. When I click a second time on this button, the loop restart to play after a setTimeout. My problem is that when I multi click (fast) on button, there is a bug in the loop. My condition doesn't work.

Is there a solution to stop effect of click during setTimeout with stopPropagation or e.preventDefault or something else?

var j = 0;
var myElements = document.querySelectorAll('.div_child');
var myButton = document.querySelector('.my_button');

var colorArray = []
for (let i = 0; i < myElements.length; i++) {
  let currentColor = getComputedStyle(myElements[i]).backgroundColor;
  colorArray[i] = currentColor;
}

function my_fonction() {
  myElements[j].style.opacity = 1;
  for (let k = 0; k < myElements.length; k++) {
    if (k != j) {
      myElements[k].style.opacity = 0;
    }
  }
  j++;
  if (j == myElements.length) {
    j = 0
  }
  playForbidden = false;
}

function setIntervalAndExecuteFn(fn, t) {
  fn();
  return (setInterval(fn, t));
}

var myIndice = j;
var myIntervalId = setIntervalAndExecuteFn(my_fonction, 1000);
var play = true;
var playForbidden = false;

myButton.addEventListener('click', function() {
  if (playForbidden == false) {
    if (play == true) {
      play = false;
      clearInterval(myIntervalId);
      if (j == 0) {
        myIndice = myElements.length - 1;
      } else {
        myIndice = j - 1;
      }
      myButton.style.backgroundColor = colorArray[myIndice]
    } else {
      play = true;
      myButton.style.backgroundColor = 'transparent';
      setTimeout(function() {
        myIntervalId = setIntervalAndExecuteFn(my_fonction, 1000);
      }, 500);
    }
    playForbidden == true;
  } else {
    return;
  }
});
.div_parent {
  position: relative;
  width: 500px;
  height: 300px;
  border: 1px solid black;
}

.my_button {
  width: 300px;
  height: 100px;
  border: 1px solid black;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: bold;
  font-size: 20px;
  padding: 20px;
  color: black;
}

.div_child {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  width: 500px;
  height: 300px;
}

.div_child_one {
  opacity: 0;
  background-color: red;
}

.div_child_two {
  opacity: 0;
  background-color: green;
}

.div_child_three {
  opacity: 0;
  background-color: violet;
}

.div_child_four {
  opacity: 0;
  background-color: rgb(104, 104, 104);
}
<div class="div_parent">
  <div class="div_child div_child_one"></div>
  <div class="div_child div_child_two"></div>
  <div class="div_child div_child_three"></div>
  <div class="div_child div_child_four"></div>
</div>
<div class="my_button">PAUSE BUTTON</div>

Upvotes: 1

Views: 623

Answers (1)

clod9353
clod9353

Reputation: 2002

The problem in your code is the setTimeout function. When you execute your setTimeout, what you are saying is "Start the interval in 0.5 seconds". But the problem is that this command is not stopped if you click on pause again really fast (within 0.5 seconds). What you can do is clearing the timeout at every click of the button. This way, you can cancel the command "Start the interval in 0.5 seconds".

You can see a working snippet of what I mean here below:

var j = 0;
var myElements = document.querySelectorAll('.div_child');
var myButton = document.querySelector('.my_button');

var colorArray = []
for(let i=0; i<myElements.length; i++){
    let currentColor = getComputedStyle(myElements[i]).backgroundColor;
    colorArray[i] = currentColor;
}

function my_fonction(){ 
    myElements[j].style.opacity = 1;
    for(let k = 0; k < myElements.length; k++){ 
        if(k!=j){  
            myElements[k].style.opacity = 0;
        }
    }
    j++;
    if(j == myElements.length){ j = 0}
    playForbidden = false;
}

function setIntervalAndExecuteFn(fn, t){
    fn();
    return(setInterval(fn, t));  
}

var myIndice = j;
var myIntervalId = setIntervalAndExecuteFn(my_fonction, 1000);
var myTimeoutId;
var play = true;
var playForbidden = false;

myButton.addEventListener('click', function(){
    clearTimeout(myTimeoutId);
    if(playForbidden == false){
        if(play == true){
            play = false;
            clearInterval(myIntervalId);
            if(j == 0){
                myIndice = myElements.length-1;
            }else{
                myIndice = j-1;
            }
            myButton.style.backgroundColor = colorArray[myIndice]
        }else{
            play = true;
            myButton.style.backgroundColor = 'transparent';
            myTimeoutId = setTimeout(function() {
                myIntervalId = setIntervalAndExecuteFn(my_fonction, 1000);
            }, 500);
        }
        playForbidden == true;
    }else{
        return;
    }
});
.div_parent{
    position: relative;
    width: 500px;
    height: 300px;
    border: 1px solid black;
}
.my_button{
    width: 300px;
    height: 100px;
    border: 1px solid black;
    font-family: Arial, Helvetica, sans-serif;
    font-weight: bold;
    font-size: 20px;
    padding: 20px;
    color: black;
}
.div_child{
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    width: 500px;
    height: 300px;
}
.div_child_one{
    opacity: 0;
    background-color: red;
}
.div_child_two{
    opacity: 0;
    background-color: green;
}
.div_child_three{
    opacity: 0;
    background-color: violet;
}
.div_child_four{
    opacity: 0;
    background-color: rgb(104, 104, 104);
}
<div class="div_parent">
    <div class="div_child div_child_one"></div>
    <div class="div_child div_child_two"></div>
    <div class="div_child div_child_three"></div>
    <div class="div_child div_child_four"></div>
</div>
<div class="my_button">PAUSE BUTTON</div>

Upvotes: 1

Related Questions