14

I am using the Mousetrap javascript library and am wanting to capture input on a specific element.

The scenario is I have a textbox for username and password which KnockoutJS binds to then when a button is pressed an ajax request is made to login the user. Now as there is no form and its not a real button its an anchor that Jquery UI turns into a button I was wondering if there was some simple way to get mousetrap to bind to the element rather than at the document level.

So for example a normal mousetrap binding would be:

Mousetrap.bind('enter', function(event) { CallSomeAjaxMethod(); });

Now that will check for any enter key press on the page (outside of a textbox element) and then do something based on it. Now the problem is in this case I am wanting to be able to ONLY capture this event whenever it is carried out within the scope of a specific element.

So for example I would be wanting to do something likethis:

var element = document.getElementById("some-element");
Mousetrap.bind('enter', element, function(event) { CallSomeAjaxMethod(); });

or maybe more fluent like:

Mousetrap.bind('enter', function(event) { CallSomeAjaxMethod(); }).on(element);

Is there any way to do this? the only way I can see to do it (with Mousetrap) currently is to have this document scope binding and just try to get the element the event was raised on if it is possible.

I know you can do this sort of thing with Jquery or vanilla js however as I already have Mousetrap loaded I would like to re-use it here if possible.

Grofit
  • 17,693
  • 24
  • 96
  • 176

2 Answers2

14

I wanted to follow up on this. As of version 1.5.0 (released today), this is now possible natively in Mousetrap.

For your specific example above all you have to do is

var element = document.getElementById("some-element");
Mousetrap(element).bind('enter', function(event) { CallSomeAjaxMethod(); });

You can also create a new instance of mousetrap to bind multiple things.

var mousetrap = new Mousetrap(element);
mousetrap.bind('space', _handleSpace);
mousetrap.bind('enter', _handleEnter);

Best of all it should be backwards compatible so all your existing code will still work.

Craig
  • 2,684
  • 27
  • 20
  • 2
    Have made this the new answer, thanks for updating the question. – Grofit Apr 05 '15 at 12:17
  • If I've called MouseTrap(element).bind('alt+h', function (event) { Something(); }); should I expect MouseTrap(element).unbind("alt+h"); to unbind? At the moment I just keep getting more bindings (I'm using MouseTrap with Aurelia and binding/unbinding keys using Aurelia bind/unbind events which are fired when a view is displayed/removed.) – Phil Jan 29 '16 at 12:31
  • @Craig How to achieve this in angular 2 ? – Vishal Nair May 12 '17 at 08:36
  • I wonder, what happens with mousetrap instance when element is later destroyed / removed from DOM? Should I unbind or the instrance is destroyed too? – hovado Oct 29 '19 at 07:35
7

As of right now the short answer to your question is there is no way to do this natively in Mousetrap. That being said there are a few ways you could approach this.

  1. I am not familiar with KnockoutJS, but browsers are built to let the enter key trigger a form submit so the most elegant solution would probably be to make your inputs part of a form in which case you don't need mousetrap at all to trigger the form submit. You could call e.preventDefault() on form submit to make sure it doesn't actually post back to the server.

  2. If you add class mousetrap to a text input then it will allow it to receive keyboard shortcuts. You could then check e.target in your callback to see if it came from that textarea. This would look something like this:

    html

    <input type="text" name="email" class="mousetrap">
    

    js

    var input = $('input[name=email]');
    Mousetrap.bind('enter', function(e) {
        if (e.target === input[0]) {
            console.log('triggered from input');
        }
    });
    
  3. You could overwrite the Mousetrap.stopCallback function to achieve this. This is not the prettiest solution, but should work.

    html

    <input type="text" name="email">
    

    js

    var input = $('input[name=email]');
    var _oldStopCallback = Mousetrap.stopCallback;
    Mousetrap.stopCallback = function(e, element, combo) {
        if (combo === 'enter') {
            return element !== input[0];
        }
        return _oldStopCallback(e, element, combo);
    }
    
    Mousetrap.bind('enter', function(e) {
        console.log('triggered from input');
    });
    

I am looking into different ways I can support different scopes/contexts in Mousetrap so hopefully there will be an easier way in the future.

Craig
  • 2,684
  • 27
  • 20
  • Yeah I thought this may be the case, as I took a quick look through the source code and noticed it bound the inputs at a global level, which would require a fundemental change to support what is being requested, unless you did some hybrid approach like your global plugin does where you keep a dictionary of elements with the binding type and if any of them match with the global event then you can fire it off... anyway most of my system uses Mousetrap the way its meant to be used for shortcuts and its BRILLIANT! I just didnt want to have to write a specific keycode == 13 style logic :( – Grofit Apr 26 '13 at 10:49