How to detect if the pressed key will produce a character inside an <input> text-box?

JavascriptJqueryKeyboard Events

Javascript Problem Overview


I have a regular text-box:

<input type="text"> 

I use jQuery to handle key-related events:

$("input:text").keydown(function() {
    // keydown code
}).keypress(function() {
    // keypress code
}).keyup(function() {
    // keyup code
});

The user focuses on a text-box and presses various keys on his keyboard (the usual ones: letters, numbers, SHIFT, BACKSPACE, SPACE, ...). I need to detect when the user presses a key that is going to increase the length of the text-box value. For example, the "A" key will increase it, the "SHIFT" key wont.

I remember watching a lecture by PPK where he mentioned the difference between those two. It has something to do with the event - keydown vs. keypress - and possibly with the event properties - key, char, keyCode.

Update!

I need to know this information within the keydown or keypress handlers. I cannot wait for the keyup event to occur.

Why I need this:

I have a text-box which size dynamically changes based on the user input. You can have a look at this demo: http://vidasp.net/tinydemos/variable-size-text-box.html

In the demo, I have a keydown and keyup handler. The keyup handler adjusts the text-box size based on the input value. However, the keydown handler sets the size to be 1 character larger then the input value. The reason I do this is that if I didn't, then the character would overflow outside the text-box and only when the user would let go of the key, the text-box would expand. This looks weird. That's why I have to anticipate the new character - I enlarge the text-box on each keydown, ergo, before the character appears in the text-box. As you can see in the demo, this method looks great.

However, the problem are the BACKSPACE and ARROW keys - they will also expand the text-box on keydown, and only on keyup the text-box size will be corrected.

A work-around:

A work-around would be to detect the BACKSPACE, SHIFT, and ARROW keys manually and act based on that:

// keydown handler
function(e) {
	var len = $(this).val().length;
	if (e.keyCode === 37 || e.keyCode === 39 ||
        e.keyCode === 16) { // ARROW LEFT or ARROW RIGHT or SHIFT key
		return;
	} else if (e.keyCode === 8) { // BACKSPACE key
		$(this).attr("size", len <= 1 ? 1 : len - 1);
	} else {
		$(this).attr("size", len === 0 ? 1 : len + 1);
	}
}

This works (and looks great) for BACKSPACE, SHIFT, ARROW LEFT and ARROW RIGHT. However, I would like to have a more robust solution.

Javascript Solutions


Solution 1 - Javascript

This I think will do the job, or if not is very close and will need only minor tweaking. The thing you have to remember is that you can't reliably tell anything at all about any character that may be typed in a keydown or keyup event: that all has to be done in a keypress handler. The definitive resource for key events is http://unixpapa.com/js/key.html

You also need to consider pastes, which this code won't handle. You will need to have separate paste event handler (although this event isn't supported in Firefox < 3.0, Opera, and very old WebKit browsers). You'll need a timer in your paste handler since it's impossible in JavaScript to access the content that's about to be pasted.

function isCharacterKeyPress(evt) {
    if (typeof evt.which == "undefined") {
        // This is IE, which only fires keypress events for printable keys
        return true;
    } else if (typeof evt.which == "number" && evt.which > 0) {
        // In other browsers except old versions of WebKit, evt.which is
        // only greater than zero if the keypress is a printable key.
        // We need to filter out backspace and ctrl/alt/meta key combinations
        return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8;
    }
    return false;
}

<input type="text" onkeypress="alert(isCharacterKeyPress(event))">

Solution 2 - Javascript

Here's a much simpler solution which worked well for me:

document.addEventListener('keyup', event => {
  if (String.fromCharCode(event.keyCode).match(/(\w|\s)/g)) {
    //pressed key is a char
  } else {
    //pressed key is a non-char
    //e.g. 'esc', 'backspace', 'up arrow'
  }
});

This doesn't require probing a DOM element (which would add latency and ugliness).

Updated example use:

Solution 3 - Javascript

The possible solution I can find is checking the length of key in event.

Eg:-

<input type="text" id="testId" onkeyup="keyChecking(event)" />

<script type="text/javascript">
function keyChecking(event) {

    if (event.key.length == 1) {
        alert("key produced character " + event.key);
    } else {
        alert("Key DOES NOT produce character");

        const alphabets = "AZaz09";
		const key = event.key;
        var notEvenASymbol = false;

        for (let i = 0; i < key.length; i++) {
            var charCode = key.charCodeAt(i);
            if ((charCode >= alphabets.charCodeAt(0) && charCode <= alphabets.charCodeAt(1)) ||
                (charCode >= alphabets.charCodeAt(2) && charCode <= alphabets.charCodeAt(3)) ||
                (charCode >= alphabets.charCodeAt(4) && charCode <= alphabets.charCodeAt(5))
            ) {
                notEvenASymbol = true;
                console.log(charCode);
                break;
            }
        }

        if (notEvenASymbol) {
            alert("Key DOES NOT produce even a symbol");
        }
        console.log(event.key);

    }
}    
</script>

So, if you press any characters/symbols, the event.key will contain that character and its length will be 1. If you press character V then the event.key will have value V but if you press enter key then it will contain value Enter, if you press shift then Shift and so on. Therefore, if a key doesn't produce a character then its length will be greater than 1.

Updated

Some special keys in the keyboard produce symbol and its length may be greater than 1 so I modified the code so that it can alert even if it's not a symbol. Eg:-  its length is 2. Some mobile keyboards have shortcut keys for such symbols.

A non character/symbol key in the keyboard will always be a combination of alphabets, number characters or of both, Eg:- F2, Shift.

Thanks @Vicky Chijwani for bringing attention to this scenario.

Solution 4 - Javascript

OK, I think I've got it. The solution is a bit hackish, but actually works really well.

On keydown, do a setTimeout for 1 millisecond, that calls a function to check/change the length of your input box.

function checkLength() {
    var len = $("input:text").val().length;
    $("input:text").attr("size", len === 0 ? 1 : len + 1);
}

$("input:text").attr({
    "size": 1,
    "spellcheck": false
}).keydown(function() {
    setTimeout(checkLength, 1);
});

p { padding:50px; }
        
input { 
    border:1px solid gray;
    padding:6px; 
    font-size:26px;
    font-family:monospace;
    outline:none; 
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<input type="text"></input>

It seems to work really well, especially in a few places where your version doesn't (e.g. backspace, CTRL+V, or selecting a whole bunch of text and hitting delete)

Edit: Even setTimeout with a 0ms delay seems to work!

Solution 5 - Javascript

To detect in a keydown handler if a pressed key produces a single unicode character, you can use the ES6 unicode u flag for regular expressions.

We use the KeyboardEvent.key property, which returns the value of the pressed key. According to the docs:

> If the pressed key has a printed representation, the returned value is a non-empty Unicode character string containing the printable representation of the key.

inputElement.addEventListener("keydown", ({ key }) => {
  if (/^.$/u.test(key)) {
    // `key` matches a single unicode character
  }
});

The solution does not handle pasting...

Solution 6 - Javascript

You should use the property keyEventArgs.Key in the keydown function, this will return the numeric value that will depend on the system.

here is a link that has the different key codes for the different browsers and OS:

http://www.quirksmode.org/js/keys.html

Solution 7 - Javascript

This may not be the method that you're looking for, but you can just check the value of this.value.length in your keydown function. The return value is the length of the text in the input field BEFORE the new character is added. So if you check the length again in the keyup function, it will be greater if the user pressed a character, but the same if the user hit the shift key.

Solution 8 - Javascript

I presume you are setting up a counter on the length of an input field, in which case you don't need to be so fancy, you can just keep assigning the length of the field to a variable, and when the user gets to your max length only allow them to press delete or backspace like so:

$("input:text").keypress(function() {
var current = $(this).val().length;
if (current >= 130) {
if (e.which != 0 && e.which != 8) {
e.preventDefault();
}
}
}

You can use the current variable to display the counter as well, or do maxlength - current to do a countdown of how many charachters are left

Solution 9 - Javascript

Your goal of keeping the textbox larger than the text that has been entered into it.

I'd accomplish this by planning on having room for two additional characters (not one) in the text box. Then:

// pseudo-code.... 
old_len = textbox.value.length
keyUp function() {
  var new_len = textbox.value.length
  if (new_len != old_len) {
    old_len = new_len
    textbox.style.size = new_len + 2 // pseudo code.
  }
}

The advantage of the above is that you don't need to descend into the nether world of keycodes.

Solution 10 - Javascript

You can subscribe to "InputEvent" and then get "data" prop. For example

input.addEventListener('beforeinput', (event) => {
    const data = event.data;

    // if "data" is present - user enter some character
    // if "data" is NOT present - user tap non character key (e.g. delete, shift and other)
    if(data) {
        const isAllow = /\d/.test(data);

        if(!isAllow) {
            e.preventDefault();
        }
    }
})

More info

  1. event.data - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/data
  2. beforeinput event - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event

Solution 11 - Javascript

Not sure about all possible keyboards, but in my keyboard I noticed that combining the "keypress" event (don't use keydown or keyup) with event.key will return a character only if it's printable, the only exception is the "Enter" key which will return the word "Enter"

so I come up with the following simple solution:

document.addEventListener('keypress', (event) => {
    if(event.key && event.key != 'Enter'){
      console.log('this is a character')
    }
});

This solution seems to also ignore shortcuts like ctrl+c or ctrl+v which other answers don't handle

Note: tested in mozilla and brave, I would like to see what is the result in other browsers (please comment)

Solution 12 - Javascript

Based on other answers I created following:

export function producesCharacter(event: React.KeyboardEvent<HTMLInputElement>) {
   return !event.ctrlKey && !event.altKey && event.key.length === 1;
}

Other solutions may not work in React due to deprecation warnings

Solution 13 - Javascript

I understand that you wanted to solve it within "keyup" or "keydown" listeners. However, I came across an issue similar to this, and solved it by using the "input" eventListener. It listens for changes in the input and makes changes accordingly. Thought I would share.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionŠime VidasView Question on Stackoverflow
Solution 1 - JavascriptTim DownView Answer on Stackoverflow
Solution 2 - Javascriptmike-shtilView Answer on Stackoverflow
Solution 3 - JavascriptVisruthView Answer on Stackoverflow
Solution 4 - JavascriptJeffView Answer on Stackoverflow
Solution 5 - JavascriptvalentinView Answer on Stackoverflow
Solution 6 - Javascriptmariana sofferView Answer on Stackoverflow
Solution 7 - JavascriptJeffView Answer on Stackoverflow
Solution 8 - JavascriptLiam BaileyView Answer on Stackoverflow
Solution 9 - JavascriptLarry KView Answer on Stackoverflow
Solution 10 - JavascriptOlegView Answer on Stackoverflow
Solution 11 - JavascriptmedBouzidView Answer on Stackoverflow
Solution 12 - JavascriptKlapsa2503View Answer on Stackoverflow
Solution 13 - JavascriptpeanutzView Answer on Stackoverflow