105

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,153
  • 6
  • 31
  • 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

194

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,566
  • 7
  • 20
  • 33
pna
  • 5,282
  • 2
  • 19
  • 36
  • 21
    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
  • 27
    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) – aexl Sep 10 '19 at 11:25
93

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/

aexl
  • 3,472
  • 2
  • 36
  • 51
Iain
  • 2,091
  • 14
  • 18
  • 11
    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
  • With the current jQuery, this should be `$(obj).off()` and `$(obj).on()` respectively. Otherwise, namespacing helped and this worked. Thank you! – aexl Sep 12 '19 at 07:24
19

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
  • 191
  • 1
  • 3
12

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
  • 580,699
  • 78
  • 809
  • 825
11

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
  • 181
  • 1
  • 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
5

jQuery makes calling some function possible only once pretty easy:

function someMethod()
{

     $(obj).click(function() {});
      this.someMethod = $.noop;
}
Esailija
  • 130,716
  • 22
  • 250
  • 308
  • 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');
3

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
  • 165
  • 2
  • 6
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
  • 1,956
  • 2
  • 14
  • 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,196
  • 3
  • 26
  • 43
cscan
  • 149
  • 8
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 http://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,273
  • 13
  • 12
1

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

target.addEventListener('click', handler, {once: true});
Skav
  • 198
  • 3
  • 11
  • 2
    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
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,359
  • 5
  • 34
  • 56
  • 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