4

I want to have one button, functioning as both the "start" and "stop" button for some reoccurring timed events.

To do this, I can have a global variable on the top of everything:

toggleOn = false; 

And then, inside of <button onClick="..., I can have:

toggleOn =! toggleOn;
foo(); 
function foo() {
  // do my stuff
  if (toggleOn) {
    setTimeout(foo, 5000);
  }
}

But the problem is, I must not use a global variable to complete the same task. How should I do it? Is there a persist variable that can carry a value outside its scope?

WSBT
  • 33,033
  • 18
  • 128
  • 133
  • 4
    set a prop on the button itself, like this.busy = !this.busy; – dandavis Sep 26 '13 at 19:50
  • @dandavis This sounds brilliant. Could you elaborate it as an answer and provide some code snippet please? – WSBT Sep 26 '13 at 19:54
  • setting a property on DOM reference (i.e. this/button as dandavis suggested) will cause memory leaks in old browsers. You are better off wrapping it in a function to close scope as jods demonstrated. – cbayram Sep 26 '13 at 19:54
  • @cbayram: so will a closure from an event handler. besides, aren't those warnings for like IE6 ? and above that, leaking a boolean? man, that will crash everything in no time . or, use classList.toggle if you want to be cool. – dandavis Sep 26 '13 at 20:20
  • @dandavis, didn't mean to hurt your feelings, simply stating a fact. IE7 too some degree too, refreshing the page will clean it up. Having dealt with IE6 web applications that stay open in the browser for days/weeks, it's a reality. Bad practice is bad practice, today a boolean, tomorrow your whole data :) – cbayram Sep 26 '13 at 20:38
  • @cbayram Technology should move forward, not being held back by IE. It is though quite unfortunate that IE is still popular... depending on the case, developers might or might not care about IE at all. Luckily I don't. – WSBT Sep 26 '13 at 20:46

3 Answers3

4

This is an example for something where closures are great feature of the language.

(function()
{
    var active = false;
    myButton.addEventListener('click', function myButtonClick(event)
    {
        if (active) {
            // recursion..?
            setTimeout(myButtonClick, 5000);
        }

        active = !active;
    }
})();

More on closures here.

Community
  • 1
  • 1
nand
  • 627
  • 5
  • 11
2

Use the javascript module pattern. Something like this:

var handler = function () {
  var private_state = true;
  return function() {
    private_state = !private_state;
    if (private_state) {
      // Do something
    }
  }
}();

Use handler as your button onclick handler.

jods
  • 4,581
  • 16
  • 20
  • you're missing a couple of parens to wrap the immediate function – Nicolas Straub Sep 26 '13 at 20:17
  • funny thing is, you don't need them. – jods Sep 26 '13 at 20:26
  • go figure, I guess you learn something new every day =). Still, wouldn't that trip up older browsers? – Nicolas Straub Sep 26 '13 at 21:31
  • Not sure which browser/why. `function() {}` is an expression, let's call that `X`. Now in the statement `var y = X();`, the call `()` has always had a higher priority than the assignment `=`. JS hasn't changed in this regard for a long time. – jods Sep 26 '13 at 21:35
0

Earlier answers already noted that you could use closures to store a "private" variable that would keep track of state. Alternatively you could use HTML5 data to store this as well.

html

<button data-toggleOn="false">Click me!</button>

javascript

button.addEventListener('click', function() {
  var toggleOn = this.dataset.toggleOn = !JSON.parse(this.dataset.toggleOn);

  if (toggleOn) {
    // do stuff!
  }
});

and if you're using jQuery..

$('button').click(function() {
  var toggleOn = !$(this).data('toggleOn');
  $(this).data('toggleOn', toggleOn);
});
callmehiphop
  • 636
  • 5
  • 10