0

Let's say I have the following events:

$(button).on('mouseup', function (event1) {
    $(something).show();

    $(document).on('mouseup', function (event2) {
        $(something).hide();
    });

});

So, it shows "something" when button is clicked and hides it when the document is clicked. How can I make sure the second evnet doesn't trigger in the same event that created it? right now the something will show and hide instantly (at least on firefox).

I need to do this without any globals of any kind, preferably.

Bogdan
  • 1,869
  • 6
  • 24
  • 53
  • 3
    Setting an event handler from within an event handler is almost always wrong (as it is here). Instead of focusing on the mechanics, describe your goal. – Jon Mar 05 '14 at 14:20
  • 1
    This adds new mouseup event to the document, every time a button is released? Apart from that, did you try with `console.log(event1)` and `console.log(event2)` and see what that gets you? Event has `target` attribute in jQuery, you could use that. Still, you need to fix your code. – dkasipovic Mar 05 '14 at 14:22
  • @D.Kasipovic it's strange. I tried comparing timestamps and if i simply log the events, it shows them alright, but if I use them in an if, statement, it shows them as undefined. I'll change the question to show how; – Bogdan Mar 05 '14 at 14:24
  • @D.Kasipovic nevermind, i'm a moron. I used event.timestamp instead of event.timeStamp. It works now. I don't know if to change the question to include the answer or just answer it myself.... – Bogdan Mar 05 '14 at 14:28
  • 1
    I agree with @Jon here, you are almost certainly doing this wrong. But at the very least, you should probably replace `.on` with `.one` so that your nested event will only fire once and then be removed. Otherwise you will pile up `.mouseup` event handlers until your web page grinds to a halt. – Matt Burland Mar 05 '14 at 14:35
  • @MattBurland thanks for your answer. I didn't use .one because in my actual working code I remove all the events in a namespace, including the current one. Also I'm aware it's not usually good form to create events inside of other events but I can't avoid it here. – Bogdan Mar 05 '14 at 14:58

4 Answers4

1

How about this:

$(button).on('mouseup', function (event1) {
    $(something).show();
    event1.stopPropagation();
    $(document).one('mouseup', function (event2) {
        $(something).hide();
    });

});

stopPropagation() will stop the event from going past the button (to the document).

one() will only run the event once and then go away... can be recreated again with another click on the button.

JSFiddle

Smern
  • 18,746
  • 21
  • 72
  • 90
  • stopPropagation will allow multiple somethings to be shown if I have multiple buttons. It's a solution but Matt's is better – Bogdan Mar 05 '14 at 14:44
  • @Bogdan, you didn't mention that, however I'm not sure how Matt's would do any different... except with his, a click on the button would also hide. If you want the best solution you should show your dom (or a sample of it). Also, `show()` just shows... it wont create multiple so I'm not sure what you mean: http://jsfiddle.net/v7Yrr/1/ – Smern Mar 05 '14 at 14:47
  • Yeah sorry I was thinking ahead of myself. This was just an example, my code is way more complicated and the show() and hide() functions are replaced by callbacks passed to my component – Bogdan Mar 05 '14 at 14:53
1

Here's another solution that doesn't rely on timestamps:

$("button").on('mouseup', function (event1) {
    $("#something").show();

    setTimeout(function () {
        $(document).one('mouseup', function (event2) {
             $("#something").hide();
        });
    }, 0);

}); 

demo

Using setTimeout with a delay of 0 will make it execute the code as soon as it's finished with this event. Also note I'm using one rather than on because you only need this event handler one time and without it you will end up attaching unlimited numbers of event handlers, every single one of which will need processing when a mouseup fires anywhere on your page.

A less silly solution might look like this:

$("button").on('mouseup', function (event1) {
     $("#something").show();
});

$(document).on('mouseup', function (event2) {
    if(event2.target != $("button")[0]) {
        $("#something").hide();
    }
});

demo

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • I'll accept this answer even tho I used mine. This answer is more to the point. – Bogdan Mar 05 '14 at 14:45
  • The less silly solution is not functionally the same as the first. Also it doesn't answer the specific question. Thanks for the first one tho, didn't think about using timeout. – Bogdan Mar 05 '14 at 14:50
  • @Bogdan: *Functionally* it is the same. It achieves the same goal of having something appear when you click the button and disappear when you click anywhere else. It functions the same way. The only *technical* difference is that the second solution always attaches the `mouseup` handler to document where the first only attaches a handler if somebody clicks the button. – Matt Burland Mar 05 '14 at 14:54
0

Why don't you isolate the event like so:

$(button).on('mouseup', function (event1) {
    $(something).show();
});

$(document).on('mouseup', function (event2) {
        $(something).hide();
});
Andrei CACIO
  • 2,101
  • 15
  • 28
  • Even so, it isn't ok to place a 'mouseup' event on the whole document. It could override your button event – Andrei CACIO Mar 05 '14 at 14:32
  • It's not usually okay but I'm building a popup/popover component and I need a way to close the popover when the user clicks anywhere on the document. – Bogdan Mar 05 '14 at 14:33
  • http://stackoverflow.com/questions/14205921/popover-close-pop-over-when-clicked-outside-of-pop-over Check out this solution it seems more elegant – Andrei CACIO Mar 05 '14 at 14:37
  • Yes it's more elegant but it's also not useful to me. I'm building this for a very broad deffinition of what a popup/popover is. I'm leaving it to the user of the component to declare the show/hide functions and show/hide conditions. That's why I need this "ugly" event structure. – Bogdan Mar 05 '14 at 14:41
-1

I ended up using the event.timeStamp property to check if the two events are distinct. Also added an unbind and a namespace to the document event to prevent event stacking.

$(button).on('mouseup', function (event1) {
    $(something).show();

    $(document).on('mouseup.'+someIdentifier, function (event2) {
        if(event1.timeStamp != event2.timeStamp) {
            $(something).hide();
            $(document).unbind('mouseup.'+someIdentifier);
        }
    });

});
Bogdan
  • 1,869
  • 6
  • 24
  • 53
  • 1
    Too complicated for just hiding and showing element. – Justinas Mar 05 '14 at 14:35
  • It's not just for hiding or showing an element. The question is about differentiating the two events, and I think that for a click event, the timestamp is a pretty good differentiator. – Bogdan Mar 05 '14 at 14:37