I have a code like this and it's working, it activates on click and keyup:
Backbone.View.extend({
events: {
"click .item": "clicked",
"keyup .item": "keyupped"
},
clicked: function(a) {
var $item = $(a.currentTarget); ...}
keyupped: function(a) {
var $item = $(a.currentTarget); ...}
But I would like to make keyup to work only with space and enter and I can't get it to work. This is the idea I have, but I can't explain to myself what the event object a represents. In the simple function, it works. But in something like this, it doesn't:
keyupped: function(q) {
if(q.which == 13 || q.which == 32) {
function(a) {var $item = $(a.currentTarget); ...}
I don't understand the event object. I understand that in the keyup function it contains keycode data, but what it represents as a whole?
How should I write my keyupped function?
Found the solution. I don't know if this is the best practice, but it is working.
keyupped: function(q) {if(q.which == 13 || q.which == 32) {
this.clicked(q);
}},
Related
I'm writing a single-field form and want the Enter key to advance the form to the next input field. Since there's another form on the page, I only want the Enter key to advance the form when one of the inputs in that form is the activeElement.
I've achieved this with what seems like an extremely verbose if() statement here:
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
var ids = [ 'this', 'that', 'there', 'thing', 'other' ];
if ( document.getElementById( ids[0] ) === document.activeElement || document.getElementById( ids[1] ) === document.activeElement || document.getElementById( ids[2] ) === document.activeElement || document.getElementById( ids[3] ) === document.activeElement || document.getElementById( ids[4] ) === document.activeElement) {
if( keyCode === 13 ) {
ev.preventDefault();
self._nextQuestion();
}
}
} );
Each of the inputs are the same class: .questions. I've tried something like:
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
var ids = [ 'this', 'that', 'there', 'thing', 'other' ];
if ( document.querySelector('.questions') === document.activeElement) {
if( keyCode === 13 ) {
ev.preventDefault();
self._nextQuestion();
}
}
} );
But of course, this only accesses the first instance of .questions on the page. I don't want to iterate over nodes, as it doesn't seem much better than what I have already.
I'm a novice, and I'm looking for more concise logic.
Just check if the activeElement has the questions class.
var pattern = /(?:^|\s)questions(?:\s|$)/
if (document.activeElement.className.match(pattern)) {
...
}
squint provided an improved regex that will account for more funky situations in the classname.
Try this:
if ((" "+document.activeElement.className+" ").indexOf(" questions ") > -1) {
...
}
First of all, you can utilise classList api to check element for given className presence, e.g. document.activeElement && document.activeElement.classList.contains('question').
All the proposed approaches are really helpful and should fix the problem. On the other hand, you might want to keep you application's state in one place so you can manage it easily. By doing so, you make your application more predictable and easier to debug.
Here's an approach you might want to take:
HTML Code:
<input class="question" value="1"/>
<input class="question" value="2"/>
<input class="question" value="3"/>
<input class="question" value="4"/>
<input class="question" value="5"/>
JavaScript Code:
var component = {
activeQuestion: 0,
questionSelector: '.question',
get activeQuestionElement () {
return component.allQuestionElements[component.activeQuestion];
},
get allQuestionElements () {
return document.querySelectorAll(component.questionSelector);
},
next: function () {
var activeElement = document.activeElement;
if (activeElement && activeElement === component.activeQuestionElement && component.allQuestionElements[component.activeQuestion+1]) {
component.activeQuestion++;
component.highlightQuestion();
}
},
highlightQuestion: function () {
component.activeQuestionElement.focus();
},
install: function () {
document.addEventListener('keyup', function (event) {
if (event.which === 13) {
component.next.call(component);
}
});
document.addEventListener('click', function (event) {
var className = component.questionSelector.slice(1);
if (event.target.classList.contains(className)) {
component.activeQuestion = [].slice.call(document.querySelectorAll(component.questionSelector)).indexOf(event.target);
component.highlightQuestion();
}
})
}
};
component.install();
As you can see above, component is a single object instance that holds useful information like activeQuestion index, question selector, some computed properties that return DOM elements. The install method binds event listeners which manage the state when events occur.
Here's a demo:
http://jsfiddle.net/maciejsmolinski/xzyvye8z/embedded/result/
When you click enter when any of the fields is focused, it is going to move focus to the next one. In addition to that, clicking on any of the fields changes active question as well. You can easily alter the behaviour by modifying the code I posted above. It is just a basic skeleton you can use to build your own functionality on top of.
This is a very basic component. You can build anything on top of it. You can introduce ES6 syntax (classes, arrow functions, const instead of var) but I intentionally left that part untouched to make it easier to understand.
Hope it helps!
You guys pointed me in the right direction with document.activeElement.className, so I upvoted all who mentioned it, but the most concise solution that works here seems to be this:
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
// enter
if ( document.activeElement.className === 'questions') {
if( keyCode === 13 ) {
ev.preventDefault();
self._nextQuestion();
}
}
} );
I'll give you guys some time to critique this before I mark it as the answer...
I have the following Javascript code:
var field = document.createElement("INPUT");
field.type = "text";
field.addEventListener("blur", function() {
// stuff that works
(e ? e : window.event).stopPropagation();
return false;
};
field.addEventListener("keydown", function() {
alert("1");
if (e) {
alert("2");
} else {
alert("3");
e = window.event;
}
alert("4");
if (e.keyCode === 13) {
this.blur();
}
return true;
}
The input field is not inside a form. Pressing enter or going out of focus is meant to submit the field value to the server (existing code I can't change). The onblur event works but the onkeydown event is not. alert("1") is executed exactly but that's all. Nothing else happened.
I have tried experimenting around and my guess is that the mere existence of e is breaking the code. I have no idea how: don't all event listeners pass an e parameter to the function being called? What's going on here? How can I resolve this?
No jQuery please, it's not available.
You are not accepting an e parameter.
In your working event, you're using e if it exists (which it wont), or falling back to window.event if it doesn't:
(e ? e : window.event)
Similar checks do not exist in the broken event listener.
Make sure you accept a parameter named e:
field.addEventListener("keydown", function(e) {
... and it will be available in your code.
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.
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')
}
}
});
I'm trying to setup a text box that runs a function on keydown.
The code is:
var input = document.getElementById('Input_Number');
input.addEventListener('onkeypress', DrawDigits);
function DrawDigits(event) {
if (event && event.keyCode == '13') {}
}
Here's an example:
http://jsfiddle.net/wuK4G/
I know this is a common question, but I really can't find the answer. I've tried several methods and none of them work.
Try this:
function DrawDigits(event) {
if (event && event.keyCode == '13') {}
}
var input = document.getElementById('Input_Number');
input.addEventListener('keypress', DrawDigits);
// ^^
The eventlistener is keypress instead of onkeypress.
If you assign the eventlistener without addEventListener it is:
document.getElementById('Input_Number').onkeypress = DrawDigits
Maybe that was the confusion?
I know we can use bind paste event as below:
$('#id').bind('paste', function(e) {
alert('pasting!')
});
But the problem is, that it will call before the pasted text paste. I want a function to be triggered after the right click -> paste text pasted on the input field, so that I can access the pasted value inside the event handler function.
.change() event also doesn't help. Currently I use .keyup() event, because I need to show the remaining characters count while typing in that input field.
Kind of a hack, but:
$("#id").bind('paste', function(e) {
var ctl = $(this);
setTimeout(function() {
//Do whatever you want to $(ctl) here....
}, 100);
});
Why not use the "input" event?
$("#id").bind('input', function(e) {
var $this = $(this);
console.log($this.val());
});
This will stop user from any pasting, coping or cutting with the keyboard:
$("#myField").keydown(function(event) {
var forbiddenKeys = new Array('c', 'x', 'v');
var keyCode = (event.keyCode) ? event.keyCode : event.which;
var isCtrl;
isCtrl = event.ctrlKey
if (isCtrl) {
for (i = 0; i < forbiddenKeys.length; i++) {
if (forbiddenKeys[i] == String.fromCharCode(keyCode).toLowerCase()) {
return false;
}
}
}
return true;
});
This one will do the same for the mouse events:
$("#myField").bind("cut copy paste",function(event) {
event.preventDefault();
});
Even though the above one will not prevent right clicks, the user will not be able to paste, cut or copy from that field.
To use it after the event, like you wondered on your question, you must use JavaScript Timing Event
setTimeout(function() {
// your code goes here
}, 10);
I had the same issue, I opted to replicate the paste action through javascript and use that output instead:
var getPostPasteText = function (element, pastedData) {
// get the highlighted text (if any) from the element
var selection = getSelection(element);
var selectionStart = selection.start;
var selectionEnd = selection.end;
// figure out what text is to the left and right of the highlighted text (if any)
var oldText = $(element).val();
var leftPiece = oldText.substr(0, selectionStart);
var rightPiece = oldText.substr(selectionEnd, oldText.length);
// compute what the new value of the element will be after the paste
// note behavior of paste is to REPLACE any highlighted text
return leftPiece + pastedData + rightPiece;
};
See IE's document.selection.createRange doesn't include leading or trailing blank lines for source of the getSelection function.
No need to bind :
$(document).on('keyup input', '#myID', function () {
//Do something
});