1

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);
}
  • 1
    This line looks as if you are creating a new function object and not ending the old one: `smthing = new drawMouseMovement(data);` I have an update demonstrating one eventListener, multiple `event.targets` – zer00ne May 16 '16 at 16:02
  • 1
    Read this about the `new` operator https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new – zer00ne May 16 '16 at 16:14
  • Thanks a bunch for the help. I could slim it down to the main problem. It only ocurs when the called function did not end before calling it again. How do I force it to end? – landmann123 May 16 '16 at 19:57
  • 1
    When is comes down to counting the difference between a few hundred ms, it is almost impossible to have such precise control in a small window of time. JavaScript in general is always off by 5 to 10ms on the average. Did you take a look at that demo? I used a switch to route functions so that you are able to trigger off of one event. The way you have it now is: 1 click 4 eventListener 4 functions. My demo: 1 click 1 eventlistener 4 functions. If your draw function has an extended duration, then you should try creating a class or a function decorator. These are advanced patterns. cont--> – zer00ne May 16 '16 at 22:12
  • 1
    Class inheritance http://stackoverflow.com/q/36871299/2813224 and function decorators http://www.dofactory.com/javascript/decorator-design-pattern – zer00ne May 16 '16 at 22:20
  • 1
    See update concerning the printing function. – zer00ne May 16 '16 at 22:36
  • Thank you so much. Works like a charm! – landmann123 May 17 '16 at 10:07
  • You're welcome very much. I learned a couple of things helping you, so thank you. :-) – zer00ne May 17 '16 at 21:45

1 Answers1

1

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;
    }
Jed Fox
  • 2,979
  • 5
  • 28
  • 38
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Your first suggestion sadly didn't do the trick. I updated it anyway and included the html snippet. Could you please clarify what you mean by your last sentence? – landmann123 May 16 '16 at 14:13
  • 1
    If you grouped your eventListeners under one common parent element, there is a way to isolate each clicked element therefore preventing unwanted calls during the bubbling phase. – zer00ne May 16 '16 at 14:16
  • 1
    I noticed the if else part of `drawMouseMovement(data)` can be done maybe differently, but I am not 100% sure since it's incomplete. I will update answer with suggestion, standby.... – zer00ne May 16 '16 at 14:19
  • To clarify, the problem occurs ONLY when `t` does not reach `maxLength` before `drawMouseMovement()` is called again. So `return` has to be triggered for the instance of the function before the next instance is started. – landmann123 May 16 '16 at 14:38
  • 1
    I figured that about draw function, that's why I suggested that you drop the `else return false` and use `return false` instead. It looks like you had the `else` if there was `!t` `t = undefined` . Inside the `if(t < maxlength)...` statement is a mystery, so I don't know what exit plan you have when `t – zer00ne May 16 '16 at 16:25