112

I have the following code:

function someMethod()
{
  $(obj).click(function {});
}

someMethod is called twice and thus click event is binded twice. How can I make it bind only once?

firebird
  • 3,461
  • 6
  • 34
  • 45
  • possible duplicate of [JQuery event model and preventing duplicate handlers](http://stackoverflow.com/questions/2180326/jquery-event-model-and-preventing-duplicate-handlers) – Cees Timmerman Jul 01 '14 at 09:53

13 Answers13

197

If you can apply it, probably want to take a look at event.preventDefault and event.stopPropagation OR unbind and bind each time, within your method like

function someMethod()
{
  $(obj).off('click').on('click', function(e) {
    // put your logic in here 
  });
}
Tony Hinkle
  • 4,706
  • 7
  • 23
  • 35
pna
  • 5,651
  • 3
  • 22
  • 37
  • 23
    Be careful, because this unbinds ALL click events, and it is not always wanted behaviour. For example, when you bind something to document, you want to unbind only that one event, not all of them. – Mārtiņš Briedis Jul 20 '15 at 07:41
  • 28
    In case you do not want to accidentally unbind other click events, you might want to use `function someMethod() { $(obj).off('click.namespace').on('click.namespace', function {}); }` – Manu Sep 21 '15 at 10:12
  • @Manu you copied from [this answer](https://stackoverflow.com/a/14208653) – ᴍᴇʜᴏᴠ Sep 10 '19 at 11:25
  • be aware that some frameworks add their own onclick events (like bootstrap for instance), those events will also be lost. The namespace approach will get round this problem. – ejectamenta Jan 23 '23 at 11:09
102

In addition to pna's answer you may wish to think about namespacing your event so you do not go around unbinding all the click events accidentally.

function someMethod() {
    $(obj).unbind('click.namespace').bind('click.namespace', function() { });
}

https://api.jquery.com/event.namespace/

ᴍᴇʜᴏᴠ
  • 4,804
  • 4
  • 44
  • 57
Iain
  • 2,259
  • 1
  • 16
  • 18
  • 12
    This should be the accepted answer. Unbinding *all* click events may break things easily - especially if its a complex project with lots of jQuery going on. Thanks for that! Didn't know about this simple namespace solution!! – Simon Steinberger Jun 25 '14 at 12:11
  • 1
    With the current jQuery, this should be `$(obj).off()` and `$(obj).on()` respectively. Otherwise, namespacing helped and this worked. Thank you! – ᴍᴇʜᴏᴠ Sep 12 '19 at 07:24
  • I think this approach will not work when you pass in the events to the on function as an object list, ie. my_element.on({click: (e)=>{handler(e)}, ...) – ejectamenta Jan 23 '23 at 11:15
  • As of 2023, I'd recommend using `.off()` and `.on()` instead of `.unbind()` and `.bind()` because according to JQuery documentation, they explicitly stated that `.bind()` is deprecated and superceded by `.on()`. Furthermore, `.bind()` only works with existing elements while `.on()` works with existing elements and elements that will be created in the future – Veerakran Sereerungruangkul Apr 25 '23 at 05:30
20

There is no built in method to determine if you have already bound this particular function. You can bind multiple click functions to an object. For example:

$('#id').bind('click', function(){
alert('hello');
});


$('#id').bind('click', function(){
alert('goodbuy');
});

if you do the above when the object is clicked it will alert hello then goodbye. To make sure only one function is bound to the click event unbind the click event handler then bind the desired function like this:

$(obj).unbind('click').bind('click', function(){... });
Matt Paxton
  • 201
  • 2
  • 3
15

Or use jQuery's one() function which is similar to on() but only fires the event once, even if you bind it multiple times.

http://api.jquery.com/one/

xandrw
  • 331
  • 2
  • 8
  • 8
    Sometimes it helps. but sometimes you want the solution in which you can: ATTACH ONCE BUT USE MANY. – Rzassar Oct 19 '15 at 08:39
  • 1
    one() only binds the event once, then after being (eg.) clicked the event does not fire anymore. Normally this is not what you want, normally you want to have the event added only once, but have that it fires always. – ejectamenta Jan 23 '23 at 10:22
13

The obvious solution is to not call someMethod() twice. If you can't fix that, then you can keep a state variable so it only ever binds once like this:

function someMethod()
{
    if (!someMethod.bound) {
        $(obj).click(function() {});
        someMethod.bound = true;
    }
}

Note: this uses a property of the function itself rather than introducing a global variable to keep track of whether it's been bound. You could also use a property on the object itself.

You can see it work here: http://jsfiddle.net/jfriend00/VHkxu/.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
5

You can use this jQuery extension function.

$.fn.once = function (type, fn, uid) {
  if(uid == undefined) {
    console.error("Called $.once without uid\n", this, "\n", fn);
  }

  var dataStr = type+"-handler-"+uid;
  if(!this.data(dataStr)) {
    this.data(dataStr, true);
    this.on(type, fn);
  }
};

Instead of doing this

$("button").on("click", function(){
  alert("You Clicked On A Button");
});

Ya do this

$("button").once("click", function(){
  alert("You Clicked On A Button");
}, "btnHandler");

Now when I have a function around it

function addBtnHandler() {
  $("button").once("click", function() {
    alert("You Clicked On A Button");
  }, "btnHandler");
}

And I call it multiple times

addBtnHandler();
addBtnHandler();
addBtnHandler();

It only does it once.

Notice that the extension works by checking both uid and type. This means that you can bind different types of handlers with the same uid, you may or may not want this. To change it edit.

var dataStr = type+"-handler-"+uid;

With something like

var dataStr = "handler-"+uid;

nathnolt
  • 435
  • 6
  • 8
5

jQuery makes calling some function possible only once pretty easy:

function someMethod()
{

     $(obj).click(function() {});
      this.someMethod = $.noop;
}
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • 1
    This will only work if someMethod is method of an object or global function. – jcubic Apr 11 '14 at 14:40
  • `jQuery.noop` is only one character shorter than `function(){}`, so it's kinda silly saying it's "helping" here, just providing an empty function. Here's a non-jQuery non-method example of this general solution: `var fn = function(){ fn = function(){}; do stuff; };` – 1j01 Feb 27 '15 at 04:13
  • 1
    @1j01 Edited to use `$` instead – Esailija Feb 27 '15 at 10:21
3

You can add css class to the binded elements and then filter them out:

function someMethod()
{
    $(obj).not('.click-binded')
          .click(function {})
          .addClass('click-binded');
}

This method may be used also for plugins:

  $(obj).not('.datetimepicker-applied')
        .datetimepicker()
        .addClass('datetimepicker-applied');
2

This is a suggestion since I do not know your logic. May or may not work for you.

Try combining jquery live() and one() functions will give you a better result than event rebinds.

The special cases work when you have 2 DOM elements (parent & child). Live() at parent node makes sure event will be invoked, and then calls one() to dynamically register event which would be executed only once. (this provides similar functionality like rebinds).

Bamboo
  • 2,046
  • 2
  • 16
  • 21
2

If you want to bind to the object only once, you should implement a flag and stick to that object.

For example:

if($('#id') && $('#id').data('done') == null)) {
    $('#id').bind('click', function() {
        alert('hello');
    });

    $('#id').data('done', true);
}
tbraun89
  • 2,246
  • 3
  • 25
  • 44
cscan
  • 364
  • 2
  • 9
1

I was also trying to use off and on method of jquery for binding event only once with the dom element which does not exists yet or the dom element is not yet created.

$('.select').off('event').on('event', 'selector', function(e){ // });

This code was not working properly

I came across a very lucrative method that is 'one' method. It is very useful when you want to bind an event only once.

You can find the document here https://api.jquery.com/one/

This is same as method 'on' but different with its behavior with not to stick with the event for multiple selectors.

$('body').one('click', 'selector', function(){ // do your stuff here });
Ankit Vishwakarma
  • 1,573
  • 16
  • 13
1
var bound = false;

function someMethod()
{
    if(!bound)
    {
       $(obj).click(function {});
       bound = true;
    }
}

but I would probably look into why it;s being called twice before making some kind of workaround.

Kai Qing
  • 18,793
  • 5
  • 39
  • 57
  • 3
    If you're going this way consider jfriend00's solution where `bound` is attached to `someMethod()` instead of polluting global name space. – nuala Sep 27 '12 at 09:51
0

You can achieve this with pure JS, using addEventListener method and his once option

target.addEventListener('click', handler, {once: true});
Skav
  • 346
  • 4
  • 11
  • 4
    This causes the event to become unbound after the element has been clicked once. This doesn't solve the problem of events being bound multiple times. – Top Cat Mar 26 '18 at 14:24