55

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.

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385

16 Answers16

33

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))">
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 2
    This is some very useful information. I have updated my demo with my own workaround and it works well enough, but I will certainly test your code, too. – Šime Vidas Nov 15 '10 at 13:25
  • @Šime: I'll repeat my main message, which is that you simply cannot reliably detect anything about characters with the `keydown` and `keyup` events. – Tim Down Nov 15 '10 at 14:58
  • I expanded my work-around. I read the event.keyCode property inside the keydown handler to detect non-character keys. I put it up here: http://vidasp.net/tinydemos/dynamic-textbox.html It works splendid. – Šime Vidas Nov 15 '10 at 15:10
  • 7
    arrow keys gives a number in `evt.which` – vsync Mar 01 '13 at 17:10
  • @vsync: For some events and some browsers, yes, but not in current browsers in the `keypress` event. – Tim Down Mar 01 '13 at 18:13
  • In addition to paste events, I think you would also need to consider cut events. (But thanks for the useful key event checker.) – Paul Lynch Dec 10 '13 at 21:41
  • @TimDown Why do you exclude the alt key? For me "alt+someKey" also produces characters, such as ¬œ∑... Here's a fiddle with your code: https://jsfiddle.net/0dujy87x/ , it prints false for alt+key even though something is inserted. – bersling Feb 14 '19 at 16:25
  • KeyboardEvent.which is deprecated according to MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which – Koert van Kleef Apr 11 '19 at 08:54
  • I check the event.key.length, if the size is equals to one, then very probably it will be a character. – João Pedro Schmitt Jul 18 '19 at 19:56
28

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:

mike-shtil
  • 623
  • 5
  • 13
  • 2
    Would be interesting to know why this answer isnt higher...Great solution! – nlv Nov 22 '17 at 15:47
  • 2
    Unfortunately this does not handle certain multi-byte characters like emoji. – Danilo Bargen Nov 22 '18 at 14:35
  • how would one call this javascript function? does the caller need the event as parameter? – Martin WasGehtSieDasAn Dec 12 '19 at 14:27
  • @MartinWasGehtSieDasAn I updated my code example to include the event handler. Hope that answers your question. – mike-shtil Dec 15 '19 at 15:54
  • Good one, but IE11 doesn't support `keyCode`. And some of us still have to support it :( – Pierre Henry Jul 16 '20 at 20:08
  • 4
    While this definitely helped me in my personal issue, this answer has another issue: Any special character like a period, comma, colon, etc., is not covered, despite them increasing the length of the string. – Timmiej93 Sep 24 '20 at 18:59
  • 3
    Use `.key()` instead of `.keyCode()`. – Tigerrrrr Nov 17 '20 at 10:23
  • 1
    is this basically the same as checking the length? or even just going "if (e.key.length < 2)" (I'd say == 1 but I want to count space) and @NeilVarnas perhaps the asker already accepted the other one as the answer before seeing this one. – Raphael Morgan Nov 24 '21 at 06:16
12

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.

Visruth
  • 3,430
  • 35
  • 48
  • 3
    Not that it matters to me, but interesting trivia: `"".length` will give you 2 though it is visually only a single "character" :P – Vicky Chijwani Oct 05 '17 at 23:04
  • Very interesting! A thought: Depending on what you're after, you might not want things like CTRL+A, CTRL+C resulting in "key produced character a", "...c", etc – Ben Philipp May 07 '21 at 21:44
  • Oh, important: Combination characters that may stand alone "if pressed" (pressed twice, for example), so to speak, like accents [´`^] will result in a false negative. This is tricky, since the first press is dead, but a second press results in a keycode for these examples. Then, of course, there is the hell of _actually_ combined results. This is a nightmare – Ben Philipp May 07 '21 at 22:03
  • If the user presses a shortcut such as Ctrl+C, the `c` will have a length of 1. (this isn't the only answer that didn't consider shortcut keys.) – Qwertie Jul 16 '21 at 03:04
11

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...

valentin
  • 570
  • 7
  • 12
  • This is good for me because it triggers `false` in case the pressed key was the "Enter" key – Normal Sep 26 '22 at 08:13
4

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!

Sumit
  • 2,242
  • 1
  • 24
  • 37
Jeff
  • 12,147
  • 10
  • 51
  • 87
  • @Jeff I'm sorry but I must insist that the text-box keeps the size of the input at all times. In your demo, the size of the text-box is 1 character larger than the input. I have updated my demo with my work-around. It satisfies my above mentioned criteria, but it is still just a work-around, since I have to hard-code all the special characters into the JS code. – Šime Vidas Nov 15 '10 at 12:52
  • @Sime: The only reason it does this is because I copied the code directly from your website, where it uses len+1. I changed it to len. http://jsfiddle.net/rygar/e2wQM/3/ – Jeff Nov 15 '10 at 14:59
  • @Jeff Do you notice the weird twitch in the text-box when you type a character? That shouldn't happen. – Šime Vidas Nov 15 '10 at 15:05
  • @Sime No, I don't. Could you be more specific? To my eyes, my demo exhibits the same behavior as your work-around for alphanumeric characters, but doesn't exhibit the weird behavior from non alphanumeric characters, ctrl+v, etc – Jeff Nov 15 '10 at 15:11
  • @Jeff I expanded my own work-around. Check it out here: http://vidasp.net/tinydemos/dynamic-textbox.html It works great :) Thanks anyway. – Šime Vidas Nov 15 '10 at 15:11
  • @Jeff The twitch does definitively occur on my machine in each browser. Try typing in different characters to see it. – Šime Vidas Nov 15 '10 at 15:18
  • @Sime Ah, I notice it now only when typing quickly. However, I may note that your work-around still does not work with ctrl+a/c, deleting selecting text, or ctrl+v! – Jeff Nov 15 '10 at 15:24
  • @Jeff +1 Yes, great remark. I have updated my work-around - it disregards Ctrl+key and Alt+key inputs now. I believe that it works now. http://vidasp.net/tinydemos/dynamic-textbox.html – Šime Vidas Nov 15 '10 at 15:41
  • Sorry to be so critical, but this doesn't work for me-- I'm using a Mac, we do Command+a/c/v! But still, selecting a bunch of text and then hitting the delete key doesn't change the size until keyup. But hey, your new version looks a lot better than the original, so I guess it just depends on how much of a perfectionist you want to be! – Jeff Nov 15 '10 at 15:44
  • FYI, http://jsfiddle.net/e2wQM/7/ seems to work really well in Safari and Firefox (no more flicker), but has some kinks in Chrome. Haven't tested IE. Would probably work better if you amalgamate the two methods. – Jeff Nov 16 '10 at 00:46
  • The regex would accept TAB as key, though tab does not produce a char in an input element, instead it switches the input. – John Oct 28 '21 at 02:40
3

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

mariana soffer
  • 1,853
  • 12
  • 17
  • @pst: IE not supporting `charCode` is not a serious problem since it provides a character code in the `keyCode` property of `keypress` events. – Tim Down Nov 15 '10 at 01:21
1

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.

Jeff
  • 12,147
  • 10
  • 51
  • 87
  • The thing is, I need to know whether the key will increase the lenght inside the keydown or keypress handlers. I cannot wait for the keyup handler to occur. – Šime Vidas Nov 14 '10 at 21:30
  • Oh, I see... You could check whether the character code is within a certain range of printable characters, or use regexs. There might be a more parsimonious way, though. – Jeff Nov 14 '10 at 21:33
1

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

Liam Bailey
  • 5,879
  • 3
  • 34
  • 46
  • In this case, "current" actually reflects the length of the text field before the new character is entered, so your counter will be one less than it should be (until keyup). – Jeff Nov 14 '10 at 21:43
  • Nope. I really need to know, for every pressed key, whether that key will increase the length of the text-box value. – Šime Vidas Nov 14 '10 at 21:47
  • What about all the other keys such as home, end, copy, paste, paging, up, down etc? – Sam Plus Plus Sep 10 '14 at 18:51
1

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.

Larry K
  • 47,808
  • 15
  • 87
  • 140
  • No, the text-box has to keep the same size as the input. I enlarge the text-box size only between the keydown and keyup event because I anticipate the new character. I updated my demo - check out my workaround in it - this is how I would like it to look like. However, in my workaround, I hard-code the special keys. – Šime Vidas Nov 15 '10 at 12:55
1

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)

medBouzid
  • 7,484
  • 10
  • 56
  • 86
1

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

Klapsa2503
  • 829
  • 10
  • 33
0

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
Oleg
  • 1,044
  • 1
  • 14
  • 10
0

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.

peanutz
  • 346
  • 3
  • 6
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/31281533) – schlenger Mar 16 '22 at 12:54
0

You could do something like this:

$("input:text").on("input", (e) => {
  e.target.size = e.target.value.length
})

Because: "The input event fires when the value of an , , or element has been changed."

This will also handle pasting and deleting.

One thing: In your example you are incrementing the size by 1 per character. The problem with this approach is that character width is variable and dependent on the font used. It won’t be totally accurate, but will still work

Dan Christos
  • 71
  • 1
  • 2
0

You can detect key like "shift" and remove before display

//remove some text in blacklist from input
function removeFromString(words, str) {
    return words.reduce((result, word) => result.replaceAll(word, ''), str)
}


var pureInput = "";



document.addEventListener("keydown", function(event) {

const black_List_Keys = ["Shift","Control","Alt","Enter","Backspace"];

pureInput = removeFromString(black_List_Keys,pureInput+event.key);

document.getElementById("demo").textContent = pureInput;


});
<h1 id="demo">Press Keys</h1>

<p>Try type this word using shift And char  like : <br> <b>T</b>ail<b>W</b>ind</p>
borma425
  • 336
  • 5
  • 17
  • Yeah but where do I find an exhaustive list of all possible keys to blacklist? I just found a few including CapsLock, NumLock, Delete, Home, End... I suppose I could press every key on *my* keyboard and blacklist the appropriate ones, but what about keyboards I don't have which may have other keys I know nothing about? – Michael Jul 03 '22 at 18:29
  • @Michael you can look at this lists : https://www.computerhope.com/keys.htm – borma425 Jul 04 '22 at 08:06
  • https://github.com/drmingdrmer/cheatsheet/blob/master/sheets/unicode/from-xahlee-info/keyboard-keys.txt – borma425 Jul 04 '22 at 08:06
  • https://github.com/jasonrudolph/keyboard/blob/main/README.md – borma425 Jul 04 '22 at 08:07
  • https://docs.github.com/en/get-started/using-github/keyboard-shortcuts – borma425 Jul 04 '22 at 08:08
-1

The following code uses the correct .key event and not the (outdated?) which event

if (/[a-zA-Z]/.test(e.key) && e.key.length == 1) {
    //keypress is character
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
rosux
  • 1
  • However pressing Ctrl+b produces e.key="b" with no additional character in the textbox. So you should control at least e.ctrlKey in addition. Regex pattern looks like excluding many print characters too. – Александр Ермолин Jul 15 '22 at 08:12