1

Consider the following code:

$(document).click(function (event) {
  console.log("Ok");
});

$(document).add($('p')).click(function onceHandler(event) {
  console.log('Clicked.');
});

I would like to make the onceHandler run only once when "p" is clicked, so each handler should run once instead of the second one running twice due to the click propagating from p to document.

event.stopPropagation() will break the first handler, so I can't use it. I've also tried:

$(document).add($('p')).click(function(event) {
  if (event.stopDoingThat) return;
  console.log('Clicked.');
  event.stopDoingThat = true;
});

which didn't work. So basically without changing anything I'm getting 2 "clicked" and one "ok". With stopPropagation - 1 "clicked", what I need is 1 "clicked" and 1 "ok"

Fluffy
  • 24,946
  • 33
  • 140
  • 221
  • Here's a generic solution using just JavaScript: http://stackoverflow.com/questions/10114978/stop-event-propagation-for-a-specific-handler – Phrogz Apr 11 '12 at 22:57

5 Answers5

2

It seems like a perfect use case for the one function from jQuery:

Description: Attach a handler to an event for the elements. The handler is executed at most once per element.

In your case, this would translate to something like the following code:

$(document).add($('p')).one('click', function onceHandler(event) {
    console.log('Clicked.');
});

More information on is to be found in jQuery docs.

Xion
  • 20,785
  • 9
  • 48
  • 77
  • I wanted the handler to execute many times for each element, just so that 1 click doesn't call it twice. Thanks for the suggestion though, I didn't know about this function – Fluffy Jan 17 '12 at 14:32
1

Have you tried:

var firstRun = true;

$(document).click(function (event) {
  console.log("Ok");
});

$(document).add($('p')).click(function onceHandler(event) {
  if(firstRun) {
    console.log('Clicked.');
    firstRun = false;
  }
});

There is probably a more elegant solution. Something along the lines of:

$(document).click(function (event) {
  console.log("Ok");
});

$(document).add($('p')).on("click.runonce", function(event) {
    console.log('Clicked.');
    $(document).off("click.runonce");
  }
});

Referenced from this other SO question

Community
  • 1
  • 1
Will
  • 471
  • 3
  • 11
  • The first one will only work the first time, I need both handlers run each time – Fluffy Jan 17 '12 at 14:25
  • Ah right, my mistake, presumed that from the name `onceHandler` that you only wanted this event to fire once. – Will Jan 17 '12 at 14:28
  • Hey, your second solution actually does work after the "runonce" change. Have never heard about namespaced events before! – Fluffy Jan 17 '12 at 14:36
  • Very handy if you are adding and removing events from the same items – Will Jan 17 '12 at 14:55
1

Not sure if this will work in all browsers, but I tested it in chrome.

$(document).click(function (event) {
  console.log("ok");
});

$(document).add($('p')).click(function onceHandler(event) {
    if(!event.originalEvent.myClickDone){
        console.log("click");    
    }

    event.originalEvent.myClickDone = true;
});

Basically, both event objects share a common originalEvent.

James Montagne
  • 73,502
  • 13
  • 107
  • 127
0

the problem is that you use the apply function on the document-object twice instead of on the paragraph-object. Change your last line so that it look like this:

$(document).click(function (event) {
  console.log("Ok");
});

$(document).add($('p').click(function onceHandler(event) {
    console.log('Clicked.');
    $('p').off('click');
  }
}));
jorgenfb
  • 2,215
  • 1
  • 21
  • 28
0

Does this not do the job? I think you could make things simple with a single event handler and the testing of the target element:

$(document).click(function(event) {
    if(event.target.tagName.toLowerCase() === "p") {
       console.log("clicked");
    }
    console.log("ok");
});

Demo.

karim79
  • 326,960
  • 63
  • 404
  • 402