mousetrap
mousetrap copied to clipboard
Unbind specific callbacks
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!
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?
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.
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!
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.
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.
+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?
+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....
another use case,
esc
used for closing the sidebar
esc
is used globally to focus out of any input
+1 for this feature
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.
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.
+1
same problem with esc
with different behaviours/namespaces
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.