mousetrap icon indicating copy to clipboard operation
mousetrap copied to clipboard

Unbind specific callbacks

Open RoboTeddy opened this issue 11 years ago • 12 comments

If there are multiple callbacks bound to a given key, e.g.

Mousetrap.bind('a', callback1)
Mousetrap.bind('a', callback2)

then using unbind nukes both of them. it'd be great to be able to unbind just one callback, e.g.

Mousetrap.unbind('a', callback2)

This seems like pretty common functionality -- you can see it in jquery's off, and in the DOM's removeEventListener, etc.

I'm happy to write the feature and submit a pull request if this is something other people want too!

RoboTeddy avatar Jan 17 '13 07:01 RoboTeddy

Mousetrap actually doesn't even allow binding of multiple callbacks right now. The second 'a' will overwrite the first one. So even before calling unbind, callback1 will never be called.

I feel like having multiple callbacks tied to the same key would be strange. Can you think of a use case where you would want to do that?

ccampbell avatar Jan 17 '13 21:01 ccampbell

I have been thinking this would be a nice feature. And it would also greatly simplify the code (i.e., you can remove the call to _getMatches just to do the de-dupe delete) and then you also wouldn't need my first Pull Request which fixes a bug the remove also can kill sequences.

That said I can't think of a clean use case that you couldn't do with a single function binding. But its a common pattern as jQuery allows multiple bindings and promises to execute them in the order they were bound.

To unbind would required to keep track of the function passed in so it can be uniquely identified at that time.

simon-o-matic avatar Jan 19 '13 06:01 simon-o-matic

Seems related to #98 and #144.

FWIW, I'd prefer to have the ability to do both kinds of binding. As a use case for multiple callbacks, I have the following scenario:

The application we're working on provides a series of modeling and design tools, like a CAD program. Within the context of this application, the user has the ability to select various visual elements by clicking on them.

One of the workflows we provide allows the user to deselect these elements by pressing the escape key. It's scoped to particular parts of the application, however, as the user is only deselecting the elements within that particular "pane" of the application.

Each time we bind the 'esc' key to a deselect action, it overrides the existing binding. When the action completes, we then have to re-bind the 'esc' key to the original context. This is heavier, and more cumbersome, than it could be.

With the ability to bind and unbind specific callbacks, we can bind the escape key to specific contexts, and unbind selectively as various contexts are activated or not. In other words, we don't need to write re-binding "glue" for every new context. The more contexts we have in our web application, the more "glue" we need to determine which context to rebind and when – doesn't scale that well.

In other words, it allows us to have multiple, unrelated functions bound to the interaction the user expects from an escape key press: "Clear my workspace of selections", for example, without requiring each of the functions to do that be wrapped in a single, monolithic function. Like a Pub/Sub pattern.

The comparison to jQuery's .on() and .off() method is an apt, one, but jQuery doesn't have as sugary a syntax as Mousetrap does, and I don't want to need jQuery for this sort of thing.

What's more, we have an API exposed to developers, providing the ability to extend the framework of our application, so having as expressive an API as possible is a high priority for us. In this way, having both Mousetrap.unbind('esc', callback) and Mousetrap.unbind('esc') is really desirable – the first of which scoped to a particular function, and the second of which unbinding all callbacks for a specific key.

Hope this makes sense. Thanks!

StrictlySkyler avatar Jul 29 '13 22:07 StrictlySkyler

I do see the value to have this for specific situations. I am definitely familiar with jQuery, I also use the same syntax in my gator library.

I just have to see if it is possible to add all this without increasing the size of Mousetrap by too much. It's only needed in very specific situations so it is a bit of a trade off. Ideally it would be an extension that offers something like this and/or scoping, but this functionality wouldn't be possible via an extension as of right now.

ccampbell avatar Jul 30 '13 01:07 ccampbell

I looked and gator, and I like it quite a bit. Now, if only it had the syntactic sugar of Moustrap! :-)

An extension makes sense, if it isn't going to be built-in. Perhaps overriding the existing methods would be the way to go.

StrictlySkyler avatar Jul 30 '13 04:07 StrictlySkyler

+1 for this feature (including allowing multiple callback binding).

The unbind(shortcut, function) definitely looks cleaner, but a possibly easier/smaller way to achieve the same thing would be to introduce the concept of namespaces into Mousetrap, à la "mod+c.namespace", "a b c.namespace", ["esc.namespace", "f"], etc.. Then binding/unbinding could look like this:

Mousetrap.bind('esc', pause);
Mousetrap.bind('esc.modal', closeModal);
Mousetrap.bind('ctrl+s.modal', saveModal);
Mousetrap.bind('esc.form', clearForm);
Mousetrap.bind(['ctrl+s.form', 's'], saveForm);

Mousetrap.unbind('ctrl+s.form'); // saveForm unbound *only from ctrl+s*
Mousetrap.unbind('.modal'); // closeModal and saveModal unbound
Mousetrap.unbind('esc'); // pause and clearForm unbound

As I said, I might consider the unbind(shortcut, function) function signature more appropriate for the OP's original issue, though maybe both are worthy features for different use cases?

Thoughts? Should I file a separate issue?

usmonster avatar Dec 06 '13 16:12 usmonster

+1 for this feature also

A fairly easy example to demonstrate why there could be a need would be a dialog ontop of a dialog (an uncommon use case but possible).

First dialog listens to escape, in the process of using the first dialog, it opens a dialog when it escapes. When the second dialog escapes, it should remove it's 'esc' handler for closing while leaving the first one intact. Right now there's overriding and collision etc....

adrian-chang avatar Apr 08 '14 02:04 adrian-chang

another use case,

esc used for closing the sidebar esc is used globally to focus out of any input

+1 for this feature

nadeemkhedr avatar Apr 14 '15 06:04 nadeemkhedr

I started work on a namespacing feature as described by usmonster: https://github.com/prominentdetail/mousetrap/commit/132d089d692a4a0224bcdec415337a3f2759c407 It currently allows binding for example: ctrl+a.name , ctrl+a.name2 , and both will fire when triggered. You can unbind them as well. It needs testing and if anyone wants to add more to it, go ahead.

prominentdetail avatar Dec 12 '15 20:12 prominentdetail

I've used a Mousetrap new instance as a namespace.

var mousetrap1= new Mousetrap
mousetrap1.bind('a', callback1)

var mousetrap2= new Mousetrap
mousetrap2.bind('a', callback2)
mousetrap2.reset()// then available the mousetrap1

But, Mousetrap.trigger doesn't notify to all instances. (Mousetrap.trigger is not a global)

Mousetrap.trigger('a') // no callbacks

+1 This feature seems still necessary also.

59naga avatar Jan 25 '16 01:01 59naga

+1 same problem with esc with different behaviours/namespaces

renatorib avatar Jan 03 '17 19:01 renatorib

To address @ccampbell's question: I understand your challenge from a user perspective, and I'd say you're right. A key shouldn't need to do two things. However: I'd need this feature to make sure my React code works properly. Since rendering may be async, order of unmounted elements may not be predictable. This could mean strange errors due to timing of called functions, which are usually nasty to debug. It may never happen on one computer, but happen frequently on another. Anecdotally, I was about to use this lib but will refrain from doing so out of fear of what will happen if I use it in a general component in a project. My generalized UI component would be used by others who may not know this limitation. Since I can't guarantee how it's used I'd rather opt for predictable behavior and attach event handlers myself instead.

scarlac avatar Feb 26 '18 11:02 scarlac