2

I have some JavaScript to execute logic i.e. doSomething() when a button is clicked. I know the class of the buttons, but there are multiple buttons on the page with this same class. The problem with my code below is that the doSomething() function is executed once for every button on the page when I only want it to execute one time only.

var myButtonClass = $(".my-button-class");
if (myButtonClass) {
    myButtonClass.click(function (event) {
        if (someCondition) {
            doSomething();
        }
    });
}

I know it would be better to select by button div, but the button div names all vary based on how many there are (i.e. #my-button-div1, #my-button-div2, etc.) and the number of buttons is indefinite.

Is there a way to only do this event once? I don't care which button in the class happens to be clicked, I just need the event to fire once and then it's done.

UPDATE: To be clear, I still want the logic to execute if the user clicks another button on the page again, so I don't want to completely unbind the event. I just don't want it to execute once for every button on the page. For example, let's say I have 4 buttons. Right now it's doing something like the following when just one button is clicked:

alert!
alert!
alert!
alert!

I only need ONE of those alerts. Basically, whenever the user clicks any of the buttons on the page, I need it to go alert! only once per click.

cvoep28
  • 423
  • 5
  • 9
  • 21

3 Answers3

2

Revised so that you don't have weird if statements, and allows for other click handlers to happily work if binded elsewhere

UPDATED:

var clickHandler = function handleClick(event) {
  doSomething();
}
var bindClicks = true;

function doSomething() {
  alert('alert!');  
}

function doTheBinding() {
  if (bindClicks) {
      $('.my-button-class').on('click', clickHandler);
      bindClicks = false;
  }
}

// as many times as you want but it will still call once
doTheBinding();
doTheBinding();
doTheBinding();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">Click me!</button>
juz
  • 609
  • 5
  • 10
  • I updated my question to be more clear. The problem here is that I still need the event to fire (just once) if any of the buttons are clicked again. – cvoep28 Mar 16 '16 at 03:25
  • How about the revised one now? – juz Mar 16 '16 at 03:32
  • it still feels like @FionaCat86 is binding the 'on' callback more than once. Which would call alert more than once. – juz Mar 16 '16 at 03:35
  • This isn't as good as my answer because in mine, there are closures which present accidental/deliberate alteration of the `bindClicks` variable by other code. – 4castle Mar 16 '16 at 04:00
  • I agree with you @4castle, global scoping isn't as great. It was to figure out what the real issue was. On the counter side, you may want to rebind later by unbinding everything and toggling the bindClicks again. – juz Mar 16 '16 at 04:09
1

You can use on() and off() to bind and unbind the event on an element respectively.

$('.my-button-class').on('click', function (event) {
    if (someCondition) {
        doSomething();

        // Unbind the event on all the elements having the class
        $('.my-button-class').off('click');

        // To unbind the event on only the clicked element
        // $(this).off('click');
    }
});

Sidenote: if (myButtonClass) { will always evaluate to true. jQuery returns an object even when the element is not found and an object is always truthy. To check if an element exists in DOM, use length property on the jQuery object $('someSelector').length > 0.

Tushar
  • 85,780
  • 21
  • 159
  • 179
1

If you give the handler a local boolean variable that is protected with a closure, you can create a function that will execute only once. See this SO answer.

Run the code snippet below to see it in action.

$(".my-button-class").click(function() {
  var executed = false;
  return function() {
    if (!executed) {
      executed = true;
      doSomething();
    }
  };
}());

function doSomething() { 
  alert("You should only see me once!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">No click me!</button>

UPDATE: To address a different issue of the click event getting bound more than once. Just do a similar thing with:

var bind = function() {
    var bound = false;
    return function() {
        if (!bound) {
            bound = true;
            $(".my-button-class").click(function() {
                if (someCondition)
                    doSomething();
            });
        }
    };
}();
bind();
bind(); // won't execute second time

var someCondition = true;
function doSomething() {
  $("#output").append("<br>alert!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="my-button-class">Click me!</button>
<button class="my-button-class">No click me!</button>
<span id="output"></span>
Community
  • 1
  • 1
4castle
  • 32,613
  • 11
  • 69
  • 106
  • The problem here is that I still want it to execute once when the user goes to click the same (or another) button again. I just don't want it to execute once for every button on the page. – cvoep28 Mar 16 '16 at 03:18
  • oh it should only execute once if done his way, if it's still executing multiple times.. you're binding the on.('click') binding multiple times – juz Mar 16 '16 at 03:21
  • @FionaCat86 That's confusing. Your question seemed pretty clear. Can you create a code snippet that has that problem? I don't see how it would execute once for every button in the first place. – 4castle Mar 16 '16 at 03:25
  • @FionaCat86 I added another code snippet to answer that question also. Would you like me to just remove the first part of my answer so you can select it as the answer? – 4castle Mar 16 '16 at 03:35