2

I'm trying to write a "Simon Says" game program with Jquery and CSS and I've come across a problem. I can't seem to make an element "flash" more than once.

I've isolated the problem into a simple model (code below), which calls the flash function recursively, similarly to in this working version: https://codepen.io/nuo/pen/DaEkq

I've tried all sorts of approaches, including CSS and Jquery animations. The crux of the matter seems to be that the second CSS change overrides the first, so only one of them happens.

what am I missing here please?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flash</title>
    <style>
    #wrapper{
        background-color: red;
        opacity: 0.6;
        width: 400px;
        height: 400px;
        margin: auto;
    }
</style>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
</head>
<body>

    <div id="wrapper"></div>

    <script>
        function flash(n){
            console.log(n);
            if(n > 0){
                $('#wrapper').css('opacity', '1')
                setTimeout(function(){
                    $('#wrapper').css('opacity', '0.6')
                }, 1000);
                flash(n-1);
            }
        }

        flash(4);

    </script>
</body>
</html>
1
  • 2
    Step through your code. When does flash(n-1) run? When do you actually want it to run? (Answers: it currently runs immediately. You want it to run when the flash has ended, ie. inside the setTimeout.) Commented Aug 2, 2018 at 13:11

3 Answers 3

2

You will need to recurse when you're done changing the opacity to 0.6. So you can use animate() and then put the recursion call inside the callback of it.

function flash(n) {
  console.log(n);
  
  if (n > 0) {
    $('#wrapper').css('opacity', '1');
    
    $('#wrapper').animate({'opacity': '0.6'}, 1000, function() {
      flash(n - 1);
    });
  }
}

flash(4);
#wrapper {
  background-color: red;
  opacity: 0.6;
  width: 400px;
  height: 400px;
  margin: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper"></div>

0

You need to run flash(n-1) after you set the opacity to 0.6, inside the timeout. As it is, the method will loop very fast creating all the timeouts, which will then all run, by appearances, at the same time.

You also need to add a delay to the secondary opacity change so there is a pause between it going to 0.6 and back to 1.

function flash(n) {
  console.log(n);
  if (n > 0) {
    $('#wrapper').css('opacity', '1')
    setTimeout(function() {
      $('#wrapper').css('opacity', '0.6');
      
      setTimeout(function(){
        flash(n - 1);
      }, 500);
    }, 500);
  }
}

flash(4);
#wrapper {
  background-color: red;
  opacity: 0.6;
  width: 400px;
  height: 400px;
  margin: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper"></div>

0
0

You need to make the flash function more dynamic, in order to do that the flash function must take an additional parameter which is ele ( the element to flash ). This way you can flash any element you want providing it must have a low opacity by default.

A second thing to maximize efficiency is to create a class ex : flashing with opacity set to 1, then simply attach that class and remove to simulate a flashing effect.

Here's a working example with no jquery dependence:

// select element
var el = document.querySelector("#el");

/*
 * @n {int} : number of flashes
 * @ele {DOMElement} : element to flash
 */
function flash( n, ele ) {

  // add flashing class 
  el.className += "flashing";

  // remove flashing class after some delay
  setTimeout(function() {
    el.classList.remove("flashing");
    n--;

    // flash it again after 500ms 
    if (n > 0) {
      setTimeout(function() {
        flash(n)
      }, 500)
    }
		
		// switch the flag
		else
			return false;
		
  }, 1000);

}

flash( 7, el );
#el {
  width: 100px;
  height: 100px;
  background: green;
  opacity: 0.5;
}

.flashing {
  opacity: 1 !important;
}
<div id="el">

</div>

1
  • if you want to chain flash multiple elements you have to create a flag variable set to false, and it's raised whenever you start flashing an element. this way you know if there is any animation going on before you start another one Commented Aug 2, 2018 at 14:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.