9

It looks like key-press can only be executed on a focus element? I don't fully buy into that, there has to be a way to execute a key-press event similar to a click event?

I have a view that works with one item at a time. I have a mouseenter - mouseleave function that adds a class to the item the mouse is over. When the item receives that class I want to be able to use a key-press event to run a function on that item.

Obviously this is a slight obstacle but Id like to find out what I need to do. Below is an example view.

var PlayerView = Backbone.View.extend({
    tagName: 'div',

    events: {
        'click .points, .assists, span.rebounds, span.steals':'addStat',
        'mouseenter': 'enter',
        'mouseleave': 'leave',
        'keypress': 'keyAction'
    },

    enter: function() {
        this.$el.addClass('hover');
    },

    leave: function() {
        this.$el.removeClass('hover');
    },

    keyAction: function(e) {
        var code = e.keyCode || e.which;
        if(code == 65) { 
            alert('add assist')
        }
    }
});

So there isn't much logic here, but I am thinking I would write something like this

    keyAction: function(e) {
        var code = e.keyCode || e.which;
        if(code == 65) { 
            var addAssist = parseInt(this.model.get('assists')) + 1;        
            this.model.save({assists: addAssist});
        }
    }

Basically If I could figure out how to fire that keyAction method I should be good to go. So what are some caveats I am missing in executing some code like this? I am sure there are a few.

I do understand some of what is wrong with this code, it has no way of knowing when we run keypress in that view, I would have to add a conditional or something to find the active class, so when I execute the keypress it knows what model I am talking about, very vague description here but I get there is something wrong I am just not sure how to do this?

My solution

initialize: function() {
    this.listenTo(this.model, "change", this.render);
    _.bindAll(this, 'on_keypress');
    $(document).bind('keydown', this.on_keypress);
},

enter: function(e) {
    this.$el.addClass('hover');
},

leave: function(e) {
    this.$el.removeClass('hover');
},

on_keypress: function(e) {
    // A for assist
    if(e.keyCode == 65) { 
        if(this.$el.hasClass('hover')) {
            var addThis = parseInt(this.model.get('assists')) + 1;        
            this.model.save({assists: addThis});
        }
    }
    // R for rebound
    if(e.keyCode == 82) { 
        if(this.$el.hasClass('hover')) {
            var addThis = parseInt(this.model.get('rebounds')) + 1;        
            this.model.save({rebounds: addThis});
        }
    }
    // S for steal
    if(e.keyCode == 83) { 
        if(this.$el.hasClass('hover')) {
            var addThis = parseInt(this.model.get('steals')) + 1;        
            this.model.save({steals: addThis});
        }
    }
    // 1 for one point
    if(e.keyCode == 49) { 
        if(this.$el.hasClass('hover')) {
            var addMake = parseInt(this.model.get('made_one')) + 1;        
            this.model.save({made_one: addMake});

            var addOne = parseInt(this.model.get('points')) + 1; 
            this.model.save({points: addOne});
        }
    }
    // 2 for two points
    if(e.keyCode == 50) { 
        if(this.$el.hasClass('hover')) {
            var addMake = parseInt(this.model.get('made_two')) + 1;        
            this.model.save({made_two: addMake});

            var addTwo = parseInt(this.model.get('points')) + 2; 
            this.model.save({points: addTwo});
        }
    }
    // 2 for two points
    if(e.keyCode == 51) { 
        if(this.$el.hasClass('hover')) {
            var addMake = parseInt(this.model.get('made_three')) + 1;        
            this.model.save({made_three: addMake});

            var addThree = parseInt(this.model.get('points')) + 3; 
            this.model.save({points: addThree});
        }
    }
}

This is cool for my app because when the user hovers over the item the user can hit a key to add data, instead of clicking.

Michael Joseph Aubry
  • 12,282
  • 16
  • 70
  • 135
  • possible duplicate of [how to capture the key event from a view ?](http://stackoverflow.com/questions/6033010/how-to-capture-the-key-event-from-a-view) – mekwall Dec 21 '13 at 06:41
  • This has been [answered before](http://stackoverflow.com/questions/6033010/how-to-capture-the-key-event-from-a-view#answer-6519908). You don't buy in to it, but that's the way keypress events work in views. – mekwall Dec 21 '13 at 06:43
  • I came across that post before asking this, and I agree it's pretty much the same concept but it's very abstract to me, maybe I am not thinking what the answers say, but it just doesn't satisfy me, or really make much sense? – Michael Joseph Aubry Dec 21 '13 at 07:16
  • Keypress events in views only work when the view has focus. If you need it to work outside of the view (when it doesn't have focus) you will have to bind it to the document instead. Does that help? – mekwall Dec 21 '13 at 07:22
  • 1
    You could just add a simple .focus() along with adding class to capture the keypress event. `this.$el.addClass('hover').focus()` should be enough to capture the keypress. – Shashank Mehta Dec 21 '13 at 15:55
  • @ShashankMehta, exactly. I have been working on an answer with this for a while. The problem is that only certain elements accept `focus`, so he might have to do a little hacking. – RustyToms Dec 21 '13 at 16:01
  • 1
    @MichaelJosephAubry, I am imagining that this is for some HTML basketball game. If that is what it is I really like the idea, you could control a player by hovering over them with the mouse and hitting keys. If you make something like that, post the link in the comments here when you are done so we can see it (and tag me, so I'll know it is up). – RustyToms Dec 21 '13 at 23:52
  • 1
    Thanks you are very close, it's a statistics app, and it's a process. I play in a men's league, and decided to put my skills to the test, we are in week three and it's my goal to keep improving the app each week. I am trying to get the app to track more data, like missed/made freethrows, twos and threes. So by having hotkeys the person tracking the stats can add the data very fast. All the user has to do is see who made the play, then hover over their name then hit the appropriate key. I decided to take on backbone.js for this project so I can use it professionally. – Michael Joseph Aubry Dec 22 '13 at 00:01

1 Answers1

15

So you are only going to be able to listen to the keypress in whichever element that you have the listener set on (or its children). And the keypress event is only going to fire if the element is focused. So I think the best solution for you would be to set focus on the element you are hovering over, then you can listen for the keypress, or better yet, listen to keydown because it behaves in a more standard way cross browser.

Here is a working JSFiddle demonstrating this technique: http://jsfiddle.net/DfjF2/2/

Only certain form elements accept focus. You can add contenteditable or tabindex attributes to the element, and that should allow pretty much any element to receive focus, but then the keydown event won't actually get fired! This is a browser specific issue. In my experience, a <span> will cause keydown and keyup events to be fired in every browser I have tested (Chrome, Firefox, IE, Safari, Android browser, Silk). So in the jsfiddle I added a span inside the target element, put focus on that, and added the keydown event listener to it.

So if you added an empty <span> into your view, your code could look something like this:

var PlayerView = Backbone.View.extend({
    tagName: 'div',

    events: {
        'click .points, .assists, span.rebounds, span.steals':'addStat',
        'mouseenter': 'enter',
        'mouseleave': 'leave',
        'keydown': 'keyAction'
    },

    enter: function() {
        this.$el.addClass('hover');
        var span = this.$el.find('span');
        span.attr('tabindex', '1').attr('contenteditable', 'true');
        span.focus();
    },

    leave: function() {
        this.$el.removeClass('hover');
        var span = this.$el.find('span');
        span.removeAttr('contenteditable').removeAttr('tabindex');
        span.blur();
    },

    keyAction: function(e) {
        var code = e.keyCode || e.which;
        if(code == 65) { 
            alert('add assist')
        }
    }
});
RustyToms
  • 7,600
  • 1
  • 27
  • 36
  • 2
    This SO answer will be helpful - [Which HTML elements can receive focus?](http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus) – Shashank Mehta Dec 21 '13 at 16:07
  • Sorry for the late reply, thanks for taking the time to write up that answer and provide a fiddle! Ill make sure to mark this, because it does work last night before I got this answer I wrote something up that works, ill post in the OP. – Michael Joseph Aubry Dec 21 '13 at 23:34