How to tell if an element accepts keyboard input? - javascript

I'm working on keyboard shortcuts for a web application and need to check if a keypress should trigger the shortcut or if it is just the user typing and should therefore not trigger the shortcut.
For example, a common pattern is to use the / or s keys to open the global search bar. Obviously this should not open the search bar if the user is typing into another input.
The ideal logic would go something like this: On keypress, check the currently focused element. If the element accepts keyboard input (can be typed into), then do nothing. If the element does not accept keyboard input, run the shortcut.
Note that checking for focusability is not enough because links and buttons are focusable, but do not accept keyboard input (in the way I mean here).
Here's what I have so far:
function acceptsKeyboardInput(element) {
return (
element.tagName === "INPUT" ||
element.tagName === "TEXTAREA" ||
element.isContentEditable
);
}
Does this approach catch every case or is there a better way to tell if an HTML element accepts keyboard input?

Will all shortcuts be more than one key? If so you can listen for input and prevent a shortcut from running with a boolean value.
var is_input = false
window.addEventListener('keydown', function (e) {
console.log(is_input)
is_input = false
})
window.addEventListener('input', function (e) {
is_input = e.constructor.name == 'InputEvent'
})
Expected output for /s while able to type would be true or false (depending on the previous is_input value) at / keypress then true at s keypress and all keys following.
Expected output for /s while not able to type would be true or false (depending on the previous is_input value) at / keypress then false at s keypress and all keys following

From what I've seen, the suggestion given in the question seems to be the best approach—with some adjustments.
The first improvement is to blacklist a set of input types that don't accept keyboard input (e.g. checkbox or radio). I find it easier and better to use a blacklist rather than a whitelist for two reasons. The first is future-proofing against newly supported input types and in case you miss one. Second and more importantly, an invalid input type defaults back to type text, which means there are an infinite number of input types that accept keyboard input.
The second change is to also include <select> elements since they can be typed into as a sort of search/quick-select functionality.
Here's the full function:
const nonTypingInputTypes = new Set([
"checkbox",
"radio",
"button",
"reset",
"submit",
"file",
]);
export function acceptsKeyboardInput(element) {
return (
(element.tagName === "INPUT" && !nonTypingInputTypes.has(element.type)) ||
element.tagName === "TEXTAREA" ||
element.tagName === "SELECT" ||
element.isContentEditable
);
}
There are a few other inputs that don't accept keyboard input but that are less widely supported by browsers (e.g. color), so I've tried to keep it to the more commonly used and widely implemented input types.

Related

js - simulate key press without specifying an element

I would like to simulate a key press in JavaScript. preferably without specifying any element.
The way I would like to do it is to use .focus() on an input and then simulate a key press like the character "a".
Just the same thing that would happen if I would press the key myself.
Searching through the internet I found no solution. It might have something to do with the combination of the simulated event and an input field. Sometimes I can catch the events, but there is no input present in the input field.
Note that this should not be considered a duplicate of Is it possible to simulate key press events programmatically? because this does not solve my problem. I have already tried it.
If you have or can include jQuery, here's the easy way
With jQuery
jQuery.event.trigger({ type: 'keydown', which: 78 }); // press n key
To simulate the keypress event within an input field, use like this
var e = jQuery.Event("keydown");
e.which = 78; // n code value
e.altKey = true; // Alt key pressed
$("#inputBox").trigger(e);
Without jQuery
var kbEvent= document.createEvent("KeyboardEvent");
var initMethod = typeof kbEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
kbEvent[initMethod](
"keydown", // event type : keydown, keyup, keypress
true, // bubbles
true, // cancelable
window, // viewArg: should be window
false, // ctrlKeyArg
false, // altKeyArg
false, // shiftKeyArg
false, // metaKeyArg
78, // keyCodeArg : unsigned long the virtual key code , else 0
0 // charCodeArgs : unsigned long the Unicode character associated with the depressed key, else 0
);
document.dispatchEvent(kbEvent);
The above code infact will not modify the input value for you, it will only simulate keystrokes
See this post for more info
How to simulate typing in input field using jQuery?
What you need is : fn.sendKeys
I wrote this answer in Is it possible to simulate key press events programmatically?, which has been updated since this question was asked (2018), and now also includes some info about updating the input element and triggering listeners (spoiler alert, it can be sometimes easy, sometimes harder, or sometimes not doable to my knowledge).
In regards to "not specifying an element", you need an eventTarget (like an element) for dispatchEvent. If you don't use any, then you're running it in the global object, equivalent to window.dispatchEvent() in the browser.
Note: when you specify an element, the event can trigger listeners in other elements (parents) as well because of event bubbling

Allow use special commands on input validating

I have a little problem with validating an input field.
Here is my validation code:
_validateInput: function(e) {
var value = e.currentTarget.value;
var key = e.which || e.keyCode;
var re = /[^0-9\.]/gi;
if (re.test(value + String.fromCharCode(key))) {
return value;
} else {
return value + String.fromCharCode(key);
}
},
The logic is the next. If user input non-digital characters, the function return current value of the input, without the last symbol.
If user type digit or dot, function return current input value + entered number.
What is my problem:
1) It not allows user to enter dot.
2) String.fromCharCode for dot character returns "¾" symbol.
3) Special symbols like backspace, tab, etc... doesn`t work.
4) Commands like Ctrl+V, Ctrl+A also does not work
Could someone help me to solve this problems? What is wrong with my RegEx?
Thanks!
P.S. Function fired on the keydown event
Unless you need to support older browsers listen for oninput instead of onkeydown and a lot of the processing will have been done for you.
http://www.w3schools.com/jsref/event_oninput.asp
This event is similar to the onchange event. The difference is that the oninput event occurs immediately after the value of an element has changed, while onchange occurs when the element loses focus, after the content has been changed. The other difference is that the onchange event also works on <keygen> and <select> elements.
Whenever oninput is triggered then check the value in the text area is valid and if it isn't then correct it. This will also let you check for multiple dots being entered (if you need to do that). For example 231.21.23 is not a valid number.

Check if INSERT mode is ON when typing in an input

I'm trying to calculate the value of an input field on various events regardless of the event caught.
I'm taking into account the events keypress keyup keydown cut paste events (Do I forget anything?).
I'm trying to determine if the text-input INSERT mode is on (default - on), or off. I cannot determine the mode by capturing a key event of that key, though I don't know what was the initial state.
Does anyone know a way? Preferably - a cross browser solution that includes IE8+, Chrome and Firefox.
Any solution, or in the words of Barney Stinson, "scam, con, hustle, hoodwink, gambit, flim flam, stratagem, and bamboozle" that could help me would be appreciated.
For IE only:
document.queryCommandValue("OverWrite");
As far as I know, Firefox and Chrome don't have overwrite mode. The insert key doesn't work in those browsers, and they are always in insert mode.
Moreover, Mac keyboard doesn't have a insert key from the beginning. So Safari also is always in insert mode.
Although I don't think you can directly access the state of the user's Insert status, you can figure out how long the strings are when the user is editing the field.
Consider this, tied with a simple <input id="input" />:
var i = document.getElementById('input');
document.onkeydown = function(e) {
var c = String.fromCharCode(event.keyCode);
if(null !== c.match(/\w/)) {
l = i.value.length;
}
}
document.onkeyup = function(e) {
var c = String.fromCharCode(event.keyCode);
if(null !== c.match(/\w/)) {
if(l === i.value.length) {
console.log("Keyboard insert might be off - length hasn't changed");
}
}
}
Note that there is a rudimentary check to try and isolate only letters and numbers (with the match against the keyCode (borrowed from here), as you don't want to perform the check against a shift key being pressed, for instance.
Basic POC here: http://jsfiddle.net/Xp3Jh/
I'm certain there are more in-depth checks you can do, but hopefully this helps!

jQuery - How to restrict input values for text box

I want to restrict input in TextBox to be either 'Y' or 'N' (any case).
How can this be done in jQuery.
I'm pretty sure that if keydown returns false, then the input is not allowed. You can do this by grabbing the key code from the event object. This doesn't prevent doing things like copy/pasting a value into the text box, though. So a better option would be a select or radio button if you want to restrict the user's input.
$("#some-selector").bind("keydown", function (e) {
return e.keyCode == 89 || e.keyCode == 78
});
As you said this does not take care of the case of copy/paste a better alternative would be to attach a change event handler and then check if its an allowed char else flag an error
$("some-selector").change( function () {
var textBoxVal=$(this).val();
if(textBoxVal!=='y' || textBoxVal!=='n')
alert("Error");
});
Note:alerts jsut an example- add a different style to the textbox or however u r handling error on ur page.

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