how to read keyboard state outside of keyboard event? - javascript

Is there a way to read keyboard state outside of a keyboard event handler? Specifically, I need to check if the shift key is down during page load. I am using jQuery.

There is no querying of keyboard state in JavaScript. Wish there was. You have listen to events and track state yourself.

This code seems to work when you're holding the shift key down but it will work on subsequent keypresses. You could unbind it after the first keypress but this also means that it will run even after the page has loaded.
$(document).ready(function() {
$(document).keydown(function(e) {
if(e.shiftKey)
alert("Shift was held");
});
});

Stolen and improved from Marko's answer.
$(document).keydown(function(e) {
if(e.shiftKey)
alert("Shift was held");
});
simply removed the $(document).ready, since you want it while page is loading, not after.

You can use my library KeyboardJS. It has a method called .getActiveKeys() which will tell you what keys are active. The library is really for binding to keys and key combos in a common sense way.
I would suggest using the event binding.
KeyboardJS.bind.key('shift', function(){
//logic on key down
}, function(){
//logic on key up
});
Note that you can do complex things too such as shift + z + x or z + x + c. This will fire if shift, z, x are pressed together, or z, x, c are pressed together.
KeyboardJS.bind.key('shift + z + x, z + x + c', function(){
//logic on key down
}, function(){
//logic on key up
});
You can read what keys are pressed outside of an event callback like this
var activeKeys = KeyboardJS.getActiveKeys();
Hope that helps,
Cheers!

It's not exactly the solution I was hopping for but here's a workaround relying on events:
var shiftKeyState = false;
var ctrlKeyState = false;
$(document).ready(function () {
$(document).bind("keydown keyup click", function (e) {
shiftKeyState = e.shiftKey;
ctrlKeyState = e.ctrlKey;
});
});

Related

Why wont this event listener work for multiple keys when in an array in javascript?

First code--> My first example does not work, and I am not sure why. I would say a condition, like if(keys[38]) where 38 in the example would be the event.keyCode. If true, it should execute the if statement.
second code--> The second example only works when there is one key being pushed, and I am not sure why it does not work for multiple keys, either. For multiple keys I would say something like if(keys[38] && keys[40]), but that will not work, it should be noted, however, that it would work for just one key, like just if(keys[38]).
I know that there are other ways to get this to work, but my objective is to figure out why these code pieces do not work.
first code piece:
var keys = [];
addEventListener("keydown", function(event) {
keys[event.keyCode] = event.type == "keydown";
event.preventDefault();
});
/*conditions*/
second code piece:
var keys = [];
addEventListener("keydown", function(event) {
keys[event.keyCode] = event.type == "keydown";
event.preventDefault();
/* conditions with multiple keys accessing by if(keys[38] && keys[40]*/
});
thanks ahead of time :)
When you press multiple keys, your addEventListener no longer listens to your first keypress. That's why your if statement is failing. You can test that out on the first example on http://api.jquery.com/event.which/ Try holding one key, then holding a second key, and then releasing the second key. You'll notice that the first key won't be logged anymore.
*edit: A way around that would be to implement it like this:
var keys = {};
$(document).keydown(function (e) {
keys[e.which] = true;
});
$(document).keyup(function (e) {
delete keys[e.which];
});
The difference is that in this case, nothing is getting overwritten, where as in your example your first key press is getting overwritten (and no longer saved) by the second key press.

How can I catch 2+ key presses at once?

Well lately i got interested in creating JS games. (not an area i have experience with but it interests me).
i know there are several gaming engines for JS out there but i dont really want to create a game. rather i am curious on how things work / how can i create one.
I have several questions:
Anyone with suggestions on where can I read about it? Prerequisite (what knowledge is needed).
I tried making a small game of something walking in a rectangular. By binding keyup to the window and checking the event.which to get the key that was pressed. I realized that if i clicked on 2 buttons same time only 1 of them is being registered. how can i overcome that?
$(window).keyup(function(event){
globalEvent = event.which;
});
To directly answer your second question.
Here is one way:
var keyPressed = {};
$(window).keydown(function(e) {
keyPressed[e.which] = true;
}).keyup(function(e) {
keyPressed[e.which] = false;
});
Now you can use keyPressed whenever you want to determine if a key is down:
// wherever
var key1 = 65, key2 = 66; // A and B
if (keyPressed[key1] && keyPressed[key2]) {
// A and B are both being pressed.
}
In order to detect multiple keys being held down, use the keydown and keyup events.
var keys = {};
$(document).keydown(function (e) {
keys[e.which] = true;
});
$(document).keyup(function (e) {
delete keys[e.which];
});

jQuery keyup only keys that affects textarea content

How can I detect if the value of a textarea changes using jQuery? I'm currently using keyup() but this triggers every key stroke of course, I dont want my code to run if it's an arrow key that was pressed or any other key that doesn't have an impact on the value of the textarea.
Take a look:
$('textarea').keyup(function() {
if (content was changed)
// Do something
});
I hope you understand. How can I do this the best way? I don't want to compare the current value to an old value to check for changes, I hope that's not the only way.
By all means the easiest way is to store old values to data and do the check every keyup. The solution is quite short and will work in any case. No need to reinvent the wheel.
$("textarea").data("oldValue", function() {
return this.value;
}).keyup(function() {
var $this = $(this);
if (this.value !== $this.data("oldValue")) {
// Do something
$this.data("oldValue", this.value);
}
});
DEMO: http://jsfiddle.net/vvbSj/
$('textarea').blur(function() {
//This will be invoked when the focus is removed
});
$('textarea').change(function() {
//Same as the blur
});
Is this what you want

Cross-browser way to get automatically repeating keydown events when key is held down

Using Javascript / jQuery, how can I get automatically repeating keydown events, or equivalent, when someone holds down a key?
What I actually want is to be able to check whether a key is down, but from other questions here it looks like that's not possible. The suggested workaround seems to be recording keydown and keyup events and then assuming the key is down if a keydown event has been recorded and no subsequent keyup.
That solution runs into a problem in my case. I am designing an online experiment. The user is supposed to hold down the "T" key for the entire experiment, never letting it up. The experiment consists of multiple trials and each trial has no access to information recorded by the previous trials. So, trial 1 could record keydown for T, but trial 2 wouldn't have access to that record and thus wouldn't know whether T was down or not.
Now, if holding down the T key would produce automatically repeating keydown events for T, I would have no problem because trial 2 would just catch the next keydown event for T to come along. But it looks like I don't get automatically repeating keydown events from holding the key down, at least in Firefox. From what I can see it seems there is variation in the way different browsers handle holding a key down. What is a good cross-browser way to solve my problem?
By the way, if it matters, I also need to be able to detect keyup and keydown events for other keys while all this is going on.
EDIT: after reading some of the comments I went back and verified that I do indeed get repeating keydown events under ordinary circumstances. But I really don't get them in the specific situation in which I need them. I've got some simple code which I think isolates the issue:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<div id="target"></div>
</body>
<script type="text/javascript">
var i;
function foo() {
i++;
$('#target').html(i);
}
function doTrial() { // do trial
i=0;
$(document).keydown(foo);
$(document).keyup(endTrial);
}
function endTrial() { // end trial
$('#target').html('');
$(document).unbind('keydown',foo);
$(document).unbind('keyup',endTrial);
doTrial();
}
doTrial();
</script>
</html>
If you press a key and hold it down, then release, then press again, the behavior is as expected, i.e. there is a counter which increments while the key is held down, disappears when it's released, and then starts incrementing again when it's pressed again.
But if you press TWO keys down, then release ONE, I would have thought that the other (not released) key would continue sending keydown events so that the counter would (after resetting) continue incrementing. In fact, that doesn't happen. Any idea why and how to make it happen?
In the browsers I tried this in, I got repeated keydown events when holding a typeable key down. I don't know if this is a problem you actually need to solve.
But, if you did think you need to solve it OR if you want to control the repeat-rate yourself, you could do it like this:
Capture the events for keydown and keyup.
On keydown, set an interval timer that fires however often you want to know that the key is still down.
On keyup for that key, stop the interval timer.
You will get repeated notification in a cross browser way as long as the key is held down.
Working demo here: http://jsfiddle.net/jfriend00/XbZYs/
var downTimer;
var lastKey;
$(document.body).keydown(function(e) {
// if not still the same key, stop the timer
if (e.which !== lastKey) {
if (downTimer) {
clearInterval(downTimer);
downTimer = null;
}
}
// remember previous key
lastKey = e.which;
if (!downTimer) {
// start timer
downTimer = setInterval(function() {
$("#result").append("+");
}, 125);
}
}).keyup(function(e) {
// stop timer
if (downTimer) {
clearInterval(downTimer);
downTimer = null;
lastKey = 0;
}
});
If you want a key to auto-repeat forever until it is raised, even if other keys are pressed and released in the meantime and you want those other keys to do their own auto-repeating, then the OS does not ipmlement that behavior so you would have to implement it yourself. You can do something like this which calls a callback function for every key repeat event:
Working demo: http://jsfiddle.net/jfriend00/aD3Eg/
// this is called for every manufactured repeat event
// the frequency of the repeat event is determined by the time value set
// on setInterval() below
function repeatCallback(key) {
$("#result").append(key + " ");
}
var repeatState = {};
$(document.body).keydown(function(e) {
var key = e.which;
// if no time yet for this key, then start one
if (!repeatState[key]) {
// make copy of key code because `e` gets reused
// by other events in IE so it won't be preserved
repeatState[key] = setInterval(function() {
repeatCallback(key);
}, 125);
} else {
// nothing really to do here
// The key was pressed, but there is already a timer
// firing for it
}
}).keyup(function(e) {
// if we have a timer for this key, then stop it
// and delete it from the repeatState object
var key = e.which;
var timer = repeatState[key];
if (timer) {
clearInterval(timer);
delete repeatState[key];
}
});
​
The repeatCallback function is called for all of these manufactured auto-repeat events and passed the key that is auto-repeating. ​
Take a look at this jQuery plugin: fastKeys I think, that it is what you want/need...

How to change characters typed in Firefox

I need to change in a text input the character '.' to ',' while typing.
In IE I change the keyCode event property in the keypress event, like this
document.getElementById('mytext').onkeypress =
function (evt) {
var e = evt || window.event;
if (e.keyCode && e.keyCode==46)
e.keyCode = 44;
else if (e.which && e.which==46) {
e.which = 44;
}
};
but it seemes that in Firefox it's impossible to change characters typed in key events.
Any suggestions?
Try this. It works on all browsers:
window.onload = function () {
var input = document.getElementById("mytext");
input.onkeypress = function () {
var evt = arguments[0] || event;
var char = String.fromCharCode(evt.which || evt.keyCode);
// Is it a period?
if (char == ".") {
// Replace it with a comma
input.value += ",";
// Cancel the original event
evt.cancelBubble = true;
return false;
}
}
};
Update: Pier Luigi pointed out a problem with the above. It doesn't take care of the caret position not being at the end of the text. It will append the command to the end even if you're inserting some text to the value.
The solution would be, instead of appending a comma, to simulate a keypress event for the comma key. Unfortunately the way dispatching of synthetic events work in different browsers seems to show a lot of variety and isn't an easy feat. I'll see if I can find a nice and generic method for it.
Assume that all properties in an Event object are immutable. The DOM spec doesn't address what happens when you change those values manually.
Here's the logic you need: listen for all key events. If it's a period, suppress the event, and manually add the comma at the cursor position. (Here's a code snippet for inserting arbitrary text at the cursor position.)
You'd suppress the event in Firefox by calling event.preventDefault(); this tells the browser not to go ahead with the default action associated with this event (in this case, typing the character). You'd suppress the event in IE by setting event.returnValue to false.
If it's not a period, return early from your handler.
Technically you just want to replace all dots with commas.
document.getElementById('mytext').onkeyup = function(){
this.value = this.value.replace('.', ',');
}
If I look at the official Document Object Model Events document, mouse events fields are defined as read-only. Keyboard events are not defined there, I suppose Mozilla followed this policy for them.
So basically, unless there is some smart trick, you cannot alter an event the way you want. You probably have to intercept the key and insert the char (raw or translated) where the caret is, the way JS HTML editors do.
Does this really need to be done on the fly? If you are collecting the information to be posted to a form or submitted to a database, would it not be better to modify the data once it was submitted? That way the user never sees the confusing change.
This is possible now by intercepting and cancelling the default keydown event and using HTMLInputElement.setRangeText to insert your desired character. This would look something like this:
document.addEventListener('keydown', $event => {
if($event.code === 'Period'){
$event.preventDefault();
let inputEl = document.querySelector("#my-input");
inputEl.setRangeText(
',',
inputEl.selectionStart,
inputEl.selectionEnd,
"end"
);
}
})
setRangeText will insert text at the cursor position in a given input. The "end" string as the last argument sets the cursor to the end of the inserted content.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setRangeText

Categories