简体   繁体   中英

jQuery callback function accumulating

I wrote some code for a custom confirm box that calls a function when confirm button (yes-button) is pressed and passes another function as a parameter and I bind it to 2 different button clicks with different functions as a parameter. For example:

$('#button1').click(function() {
    callFunction(function() { alert("test"); });
});
$('#button2').click(function() {
    callFunction(function() { alert("test2"); });
});

function callFunction(callback) {
    //code to display custom confirm box

    console.log(callback);

    $('.confirm-box .yes-button').click(function() {
        callback();
    });
}

Everything happens as expected, confirm box appears and on confirm button click I get a callback function executed and it alerts "test" (or "test2" depending on which button called the confirm box). The problem arises when I click button1 that sends a function that alerts "test", then instead of confirming I cancel that (nothing happens as expected), and then click button2 that passes alert("test2") as a callback function. Now once I press the yes-button instead of alerting just "test2", I get both "test2" and "test" alerts even though that console.log I wrote logs just the function that alerts "test2" at the time of that button2 click. It seems like these callback functions get stacked somewhere, but I don't understand where and why.

The .click() function can add more than one handler to an element, and I think that's what's happening here. Try this:

// ...
$('.confirm-box .yes-button').unbind('click').click(function() {
    callback();
});

This removes any previous click handler before applying the new one.

When you execute the code:

$('.confirm-box .yes-button').click(function() {
    callback();
});

you are binding an event handler to the .yes-button element. If you run that code twice, it will have two events bound to it. And so on.

One solution is to use .one instead, so that the event handler will be removed after the first time it is fired:

$('.confirm-box .yes-button').one("click", function() {
    callback();
});

This of course has issues if there are two confirm boxes open simultaneously or if there are two .yes-button elements, but for a simple use case it works fine.

You can do it by setting an identity by classes,

var button = $('.confirm-box .yes-button');

$('#button1').click(function() {
  button.removeClass("b").addClass("a");
});

$('#button2').click(function() {
  button.removeClass("a").addClass("b");
});

button.click(function() {
  if($(this).hasClass("a")){
     callBackForButton1();
  } else {
     callBackForButton2();
  }
});

It is a bad practice to stack up the event handler for a single element.

What is happening is that each time a button is clicked the callFunction method is executing. It runs through that code block and applies an event listener to the $('.confirm-box .yes-button') button. So clicking your button N times will apply the click listener N times as well. One solution is to store the function in a variable.

Not sure what the end goal is, but this is one solution. Another solution would be to remove buttons event listeners each time.

var functionToCallOnYes = function() {};

$('#button1').click(function() {
  functionToCallOnYes = function() {
    alert("test");
  };

});

$('#button2').click(function() {
  functionToCallOnYes = function() {
    alert("test2");
  };
});


$('.confirm-box .yes-button').click(function() {
  console.log(functionToCallOnYes);
  functionToCallOnYes();
});

Yes, extra callbacks are getting stacked up. In $('button1').click(f) , the function f will be called with no parameters every time button1 is clicked. In this case, f is callFunction -- a function that itself attaches a new handler to any .confirm-box .yes-button element each time it's invoked. So on the Nth click, you should have N-1 alerts.

To make things like this easier, you can refer to functions by name in JavaScript. So if you had function test() { console.log("test"); }; function test() { console.log("test"); }; , you could write $(".confirm-box").click(test) just once and every click on a .confirm-box from then on would print test to the console.

Usually if you have callbacks whose sole purpose is to call a callback, you can just remove that callback.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM