Track first keypress event - javascript

For reasons I have to rely on keypress instead of keydown/keyup. keypress repeatedly fires events if the key is pressed. How to track only the first press on a key?
For keydown/keyup this would not be a problem, since there is a dedicated keyup event. But this is not the case for keypress.

Check the 'repeat' property of the event (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
If it's false then it's the first event, true otherwise.
Example using jquery:
$("#someid").on("keypress", function(e){
if(!e.originalEvent.repeat){console.log("first keypress")};
})

It's true that the "repeat" attribute doesn't work in Safari. It's only available in Gecko-based browsers. In lieu of that, try this (if you absolutely MUST use "keypress" instead of the alternatives):
var lastCode = -1;
$("#someid").on("keypress", function(e) {
var code = e.keyCode || e.which;
if (lastCode !== code) {
// do something
}
lastCode = code;
});

Related

How to disable key bindings in X3DOM

I am using X3DOM for a simple game, but I can't use keyboard keys the way I want to, because X3DOM reacts to them.
Example:
window.addEventListener('keydown', event => this.onKeyDown(event));
I want to have my own key event for if keyCode == 68. That works, but X3DOM reacts to it too, by changing the navigation mode and displaying an overlay.
How can I disable this?
disclaimer: I've never used x3dom, I was just poking around in the source code
there appears to be some code which disables keypress handling
this.disableKeys = x3dElem.getAttribute("keysEnabled");
this.disableKeys = this.disableKeys ? (this.disableKeys.toLowerCase() == "true") : false;
which is later read when setting up the keypress event handler:
this.onKeyPress = function (evt) {
if (!this.parent.disableKeys) {
evt.preventDefault();
this.parent.doc.onKeyPress(evt.charCode);
}
this.parent.doc.needRender = true;
}
So it appears setting keysEnabled=... attribute can turn this off. The oddity appears to be that their logic is backwards (?) for the condition:
x3dElem.getAttribute("keysEnabled") must be truthy (that is, it must have an attribute)
if that is falsey, this.disableKeys is always false
otherwise it is equal to that attribute's value lower-cased being equal to 'true'
So to disable keypress events, use <... keysEnabled="true" ...>
I made a github issue about the backwards logic, perhaps in the future it will be keysDisabled="true" instead
update:
in the latest version the attribute has been renamed to disableKeys so use <... disableKeys="true" ...>
You can use event.preventDefault to prevent X3DOM from reacting to that key:
window.addEventListener('keydown', event => {
if ((event.which === 68) || (event.keyCode === 68)){
event.preventDefault();
//
// ...
//
}
});

Detecting ctrl + arrow keys in javascript [duplicate]

I see some similar questions here (like JavaScript: Check if CTRL button was pressed) but my problem is actually the event triggering. My js code:
// Listen to keyboard.
window.onkeypress = listenToTheKey;
window.onkeyup = listenToKeyUp;
/*
Gets the key pressed and send a request to the associated function
#input key
*/
function listenToTheKey(e)
{
if (editFlag == 0)
{
// If delete key is pressed calls delete
if (e.keyCode == 46)
deleteNode();
// If insert key is pressed calls add blank
if (e.keyCode == 45)
createBlank();
if (e.keyCode == 17)
ctrlFlag = 1;
}
}
The event triggers for any other keys except the ctrl.
I need to also trigger it for ctrl.
I can't use jQuery/prototype/whatever so those solutions are not acceptable.
So... how can I detect the ctrl?
Try using if (e.ctrlKey).
MDN: event.ctrlKey
Using onkeydown rather than onkeypress may help.
From http://www.w3schools.com/jsref/event_onkeypress.asp
Note: The onkeypress event is not fired for all keys (e.g. ALT, CTRL,
SHIFT, ESC) in all browsers. To detect only whether the user has
pressed a key, use the onkeydown event instead, because it works for
all keys.
Your event has a property named ctrlKey. You can check this to look if the key was pressed or not. See snippet below for more control like keys.
function detectspecialkeys(e){
var evtobj=window.event? event : e
if (evtobj.altKey || evtobj.ctrlKey || evtobj.shiftKey)
alert("you pressed one of the 'Alt', 'Ctrl', or 'Shift' keys")
}
document.onkeypress=detectspecialkeys
This is basically Rick Hoving's answer, but it uses keydown like Danielle Cerisier suggests
function detectspecialkeys(e) {
var evtobj = window.event ? event : e
if (evtobj.ctrlKey)
console.log("You pressed ctrl")
}
document.onkeydown = detectspecialkeys
Please note that you may have to click inside the preview box to focus before pressing ctrl

keydown event in drop down list

I am trying to create a Drop down list, that when a user holds the SHIFT key, it will select the same index on all other drop down lists.
Currently, I am doing the following:
$(document).on('keyup keydown', function (e) { shifted = e.shiftKey });
$(document).on('change', '.report_info select', function (e) {
if (shifted) {
//Code to change other drop down lists.
}
});
This only works if you press and hold the shift key before you enter the drop down list. If you are inside the DDL and press the shift key, the keyup/keydown event will not fire and shifted will remain false
Is there any way to catch the keyup/keydown event while a dropdownlist is focused?
Edit:
Looks like it might be an issue with Chrome only, Just tried adding the following, and it works in Firefox and IE, but not Chrome:
$(document).on('keyup keydown', 'select', function (e) {
shifted = e.shiftKey;
});
Here is a fiddle of it not working in chrome: http://jsfiddle.net/ue6xqm1q/4
I think #judgeja's response may be your best bet. I'm posting this as an "answer" instead of a comment, because I've done my own research to determine that absolutely no event gets fired when a select element is open in Chrome.
See Fiddle: http://jsfiddle.net/m4tndtu4/6/
I've attached all possible event handlers (I think) to the input element and both select elements.
In Chrome, you'll see many events fire when working with the input element, but you'll see no events fire when working in the first select element when it is open.
Interestingly, events do fire in Chrome if the select element has the multiple attribute or size>1.
In Firefox, you'll see events firing on all three elements.
Outside #judgeja's suggestion, your best bet may be to simulate the select element.
UPDATE
Warning: Chrome (mis)behavior differs on different platforms! In Chrome Windows a keydown event is triggered right after the select is released, while on OsX it is not. This explains why #judgeja solution worked for some, and didn't for me, while mine worked on OsX and not on Windows.
So I created an updated fiddle to merge my OsX solution with his Windows one.
http://jsfiddle.net/0fz5vcq6/5/
On platforms where the keydown is triggered uses #judgeja solution, if it is not triggered it tests for a keyup event without the previous keydown (my previous solution). It is ugly as it works only after RELEASE of the shift key, but ugly only on Chrome OsX.
var shifted = false;
var hackytimer = 0;
var lastval=null;
$(document).keydown(function(e){
if(e.which == 16){
if(Date.now() - hackytimer <200){
alert("you pressed shift inside the select (Win)");
changeAllSelects($(this).val());
} shifted = true;
}
});
$(document).keyup(function(e){
if(e.which == 16) {
if(!shifted && lastval!=null) {
alert("you pressed shift inside the select (OsX)");
$('.report_info select').each(function () {
changeAllSelects(lastval);
});
}
shifted = false;
}
});
$(document).on('change', '.report_info select', function (e) {
hackytimer = Date.now();
if (shifted) {
changeAllSelects($(this).val());
} else {
lastval=$(this).val();
}
});
function changeAllSelects(cr){
hackytimer = 0;
$('.report_info select').each(function () {
$(this).val(cr);
});
}
Credit goes mainly to #judgeja for his timer solution with some added workaround for the Mac (and other platforms that behave the same)
I still think emulating the selects with something HTML like http://gregfranko.com/jquery.selectBoxIt.js/ is cleaner as they should not interfere with keydown/keyups.
PREVIOUS SOLUTION (OsX only)
The only solution I could think of, in the total absence of any event, is to test if a shift up occurred without the previous shift down. This may work if you don't have other elements that behave the same way as the selects
http://jsfiddle.net/6jkkgx5e/
It is a bit tricky and dirty, will work AFTER the user releases the shift key
var shifted = false;
var lastval=null;
$(document).keydown(function(e){
if(e.which == 16){
shifted = true;
}
});
$(document).keyup(function(e){
if(e.which == 16){
if(!shifted && lastval!=null) {
alert("you pressed shift inside the select");
$('.report_info select').each(function () {
$(this).val(lastval);
});
}
shifted = false;
}
});
$(document).on('change', '.report_info select', function (e) {
var cr = $(this).val();
if (shifted) {
$('.report_info select').each(function () {
$(this).val(cr);
});
} else {
lastval=cr;
}
});
Should behave normally on non buggy browsers. Anyway I agree emulating the selects with something HTML like http://gregfranko.com/jquery.selectBoxIt.js/ might be the cleaner way.
Your syntax looks incorrect.
$("#target").keydown(function() {
alert( "Handler for .keydown() called." );
});
This is a pretty hacky solution to be honest, but it's a means to an ends until you hopefully find something better.
Since the problem is chrome doesn't register the keydown/keyup events on the select elements until after the dropdownlist has disappeared, we need to either
a) figure out how to make the event fire (I've no idea)
or
b) check if our conditions were met in a different order.
Chrome will fire the shift keypress event after click, so we can simply check if click was pressed immediately before this event. Since other browsers behave more expectedly we'll also leave the previous code in place.
To do this we set a timer on the click event, and then when the shift event for the select is fired, if the click event timer was also just set we should run our code here so that chrome will fire it. We reset the timer then so that it isn't fired multiple times.
NOTE: if you press shift immediately after setting the values (within whatever limit from the click you specify), it will also set them all. I don't think this is unreasonable as it actually feels quite natural when it happens.
I used the following code:
var shifted = false;
var hackytimer = 0;
$(document).on('keyup keydown', function (e) {
shifted = e.shiftKey;
});
$(document).on('keyup keydown', 'select', function (e) {
shifted = e.shiftKey;
if(Date.now() - hackytimer <200){
changeAllSelects($(this).val());
}
});
$(document).on('change', '.report_info select', function (e) {
hackytimer = Date.now();
if (shifted) {
changeAllSelects($(this).val());
}
});
function changeAllSelects(cr){
hackytimer = 0;
$('.report_info select').each(function () {
$(this).val(cr);
});
}
See working example here: http://jsfiddle.net/0fz5vcq6/2/
First of all. You should pick an event to register. Don't register to keyup and keydown the same time. You give the browser a hard time, because they affect your end result. To see what I mean, just edit the plunk, add a keyup event. The end result behaves a little sloppy.
keyup: Event fired when a key is released on the keyboard.
keydown: Event fired when a key is pressed on the keyboard.
keypress: Event fired when a key is pressed on the keyboard.
They have their differences, better stick to one, I prefer for this example to use keydown, just plays better with me, you can use keyup.
Edit: A quick note. The keyup for my example doesn't play well, because it seems, change in the selectedIndex, comes first and then the binded event. On keydown, first the event fires, does it's work and then the selectedIndex changes. To play with keyup, the code below needs some modification, that means the step is not needed, when you use keyup
I have a plnkr demo here.
I've tested it on IE10, Opera, Safari, Firefox and Chrome. As you might expect, webkit browsers, don't fire the keydown/keyup/keypress event when a select list has focus. Reason unknown for me, at the moment. In Firefox works great. On IE works partially. So, in order to achieve your goal, custom code to the rescue! I just binded a change event to the document, i hear for kewdown and change. If the event type is change, then the workaround comes into play. Here some code:
$(document).ready(function(){
$(document).on('keydown change', 'select', function(evt){
var shiftKey = evt.shiftKey;
var code = evt.keyCode || evt.which;
//if it's a change event and i dont have any shiftKey or code
//set the to a default value of true
if(evt.type === 'change' && !shiftKey && !code){
//special! only when change is fired
shiftKey = true;
code = true;
}
//if shift key
if(shiftKey && (code === 40 || code === 38 || code === true)){
var target = $(evt.target);
//if code is not true means it is not a change event, go ahead and set
//a step value, else no step value
var step = (code !== true) ? (code === 40) ? 1 : -1 : 0;
var index = target[0].selectedIndex + step;
//just to keep the lower and upper bound of the select list
if(index < 0){
index = 0;
}else if(index >= target[0].length){
index = target[0].length - 1;
}
//get all other select lists
var allOtherSelects = target.closest('div').siblings('div').children('select');
//foreach select list, set its selectedIndex
$.each(allOtherSelects, function(i, el){
el.selectedIndex = index;
});
}
});
});
Chrome hack: You can set custom event for document. And firing this event when press the shift key inside the DDL. Jquery firing trigger can pass custom params.
Check the working JS FIDDLER
Try the following script for your scenario
<script type="text/javascript" language="javascript">
window.shifted = false;
$(document).on('keyup keydown', function (e) { shifted = e.shiftKey; });
$(document).on('change', 'select.report_info', function (e) {
var cr = $(this).val();
if (shifted) {
$('.report_info').each(function () {
$(this).val(cr);
});
}
});
</script>
Bryan,
you can try the following way to do this. I tested on Jsfiddle it working in your way If I got your question correctly.
var shifted = false;
$(document).on('keyup keydown', function (e) { shifted = e.shiftKey; });
$("select").on('keyup keydown', function (e) {
shifted = e.shiftKey;
});
$('.report_info select').on('change', function (e) {
var cr = $(this).val();
if (shifted) {
$('.report_info select').each(function () {
$(this).val(cr);
});
}
});
Please let me know if it works for you.
Hello that was not working because of no focus on your select which has keydown bound
try this
http://jsfiddle.net/t8fsuy33/
$('select').first().focus();
Hop this thing help you out.. :)
var onkeydown = (function (ev) {
var key;
var isShift;
if (window.event) {
key = window.event.keyCode;
isShift = window.event.shiftKey ? true : false;
} else {
key = ev.which;
isShift = ev.shiftKey ? true : false;
}
if ( isShift ) {
switch (key) {
case 16: // ignore shift key
break;
default:
alert(key);
// do stuff here?
break;
}
}
});
I found a very unique solution to this issue specifically for Chrome. It appears Chrome shifts outside the normal dom for select elements when they have focus so you never get the onkey(down|press|up) events to capture the keycode. However if the size of the select box is >1 then it works. But anyone who wants an actual drop down box instead of what looks like a combo box can solve this issue with this code. In my case I was trying to prevent the backspace key from going back to the previous browser page.
Javascript looks like this:
$(document).ready(function() {
$('select').keypress(function(event)
{ return cancelBackspace(event) });
$('select').keydown(function(event)
{ return cancelBackspace(event) });
});
function cancelBackspace(event) {
if (event.keyCode == 8) {
return false;
}
}
Then HTML looks like this:
<select id="coaacct" style="border:none;width:295px;" onclick="if (this.size){this.size=''}else{this.size='20'};">
I use the onclick event to change the size of the select box to 20 if it has no size and change it back to nothing if it has a size. This way it functions like a normal select dropdown but because it has a size greater than 1 when you are selecting the option it will detect keycodes. I didn't see this answered adequately anywhere else so I thought I would share my solution.
If you need to do things with a dropdown that are this granular, it's likely not worth it to use the native <select> element as-is. In this thread alone, numerous implementation differences are discussed, and there are plenty more that are outside the scope of this discussion but will also likely affect you. There are several JS libraries that can wrap this control, leaving it as-is on mobile (where the native control is actually needed) but emulating it on desktop, where the control doesn't really do much that can't be emulated in JS.
bootstrap - doesn't automatically wrap native control for mobile
dropdown.js
dropdown.dot.js

JS bind a keyboard shortcut to a function

function initKeys() {
$(document).bind("keydown", "U", LoadPlayer);
}
window.onload = initKeys;
I want to execute the function 'LoadPlayer' on pressing the u key.
What I'm getting is that for any pressed key the 'LoadPlayer' is executed.
The HotKeys library is added like this:
<script language="javascript" type="text/javascript" src="./libraries/jquery.hotkeys.js"></script>
But it cannot be found. I've putted it in the exact same place as other libraries. No problem with other ones
What am I doing wrong?
Try this, it should work.
It checks the key pressed in an anonymous function (so that you can add as many hotkeys as you need).
$(document).ready(function(){
$(document).bind("keydown", function(e){
e = e || window.event;
var charCode = e.which || e.keyCode;
if(charCode == 85) LoadPlayer();
});
});
Demo: http://jsfiddle.net/HULgw/
(click in the result block after running to make the keydown event listened :) )
You're binding the keydown event on all keys. that "U" is the parameter you will pass to the handler, loadPlayer (q.v http://api.jquery.com/bind/). Instead bind keydown directly, and filter inside it on the keycode.
Try deleting ./ from your javascript src path. Also check file permissions on your hotkeys library.

jQuery plugin not firing correction keydown or idle events

I am programming a jQuery plugin which tracks specific events. I have provided 2 JSFiddle examples for the sanitised code to assist at the end of the question.
I am struggling to fathom why 2 particular events are not firing. The first function tracks when the user triggers the backspace or delete keys within an input or textarea field. The code for this:
// Keydown events
$this.keydown(function (e) {
var keyCode = e.keyCode || e.which;
// Tab key
if (e.keyCode === 9) {
alert('tab key');
} else if (e.keyCode === 8 || e.keyCode === 46) { // Backspace and Delete keys
if ($this.val() !== '') {
alert('Backspace or delete key');
}
}
});
I only wish to track the error-correction keys when a field is not empty. The tab key in the above example works as expected within the conditional statement. The backspace and delete keys do not work when inside the plugin and targeting the element in focus.
The second event not firing is tracking whether a user becomes idle. It is making use of jQuery idle timer plugin to manipulate the element in focus.
// Idle event
$this.focus(function() {
$this.idleTimer(3000).bind('idle.idleTimer', function() {
alert('Gone idle');
});
}).focusout(function() {
$this.idleTimer('destroy');
});
With both of these events I have refactored the code. They were outside of the plugin and targeted $('input, select, textarea') and worked as expected. I have brought them inside the plugin, and set them to $(this) to manipulate elements currently in focus. For most of the functions, this has worked without fault, but these 2 are proving problematic.
The first JSFiddle is with the 2 functions inside the plugin. tab works, whereas the correction keys do not. Strangely, in this example the idle function is firing (it does not in my dev environment). As this is working in the JSFiddle, I accept this may be difficult to resolve. Perhaps suggestions on handling an external plugin within my own to remedy this?
Fiddle 1
The second JSFiddle has taken the backspace and delete key functionality outside of the plugin and targets $('input, select, textarea') and now works.
Fiddle 2
For Fiddle1:
if ($this.val() !== '') {
alert('Backspace or delete key');
}
Look at what $this actually is.

Categories