19

Is it possible to change the character which has been entered on keypress, without doing it manually?

For example, if I want to force uppercase letters based on some condition, it'd be nice to do the following:

function onKeypressHandler(e)
{
    if ( condition )
    {
        e.which -= 32;
    }
}

But of course that doesn't work.

NOTE: This is not an across the board uppercasing, but only specific characters.

Maybe I want to say if ( e.which >= 97 && e.which <= 102 ) or if ( Wind.Direction == 'South' ) or whatever - the condition itself is not important, but the uppercasing must only apply to the current character not the entire input.


I can do it by manually appending the changed character, but this is an ugly and messy way of doing it, and probably slower than it could be.

function onKeypressHandler(e)
{
    if ( condition )
    {
        $j(this).val( $j(this).val() + String.fromCharCode( e.which - 32 ) );
        return false;
    }
}

A specific flaw with this method - if selecting all input text and entering a key, if it drops into this then it doesn't remove existing content, but simply appends to the content the user wanted removed. (Would need to investigating detecting any selected text to solve that, which makes this one even uglier.)

Can anyone provide a better solution?

Peter Boughton
  • 110,170
  • 32
  • 120
  • 176
  • How would you handle pasting, undo/redo, dragging and dropping or other instances of multiple characters being entered at the same time, or isn't that important? – Andy E Oct 13 '10 at 11:45
  • Andy, that's not hugely important here, but would still be useful to handle - I guess it'd have to involve walking the string manually, within the onchange event? – Peter Boughton Oct 13 '10 at 11:56
  • 1
    Or the *oninput* and *onpropertychange* events. I outlined these on my blog a short while ago - http://whattheheadsaid.com/2010/09/effectively-detecting-user-input-in-javascript and I have an almost complete jQuery plugin for it in the works. – Andy E Oct 13 '10 at 12:06

6 Answers6

19

The following will do the job. It's based on an answer I wrote to another question. Customize the transformTypedChar function to suit your needs; my example capitalizes only the letters a-g.

If you need this on a textarea rather than an <input type="text"> then be aware that there are issues in IE <= 8 with line breaks that the following code doesn't handle for the sake of brevity. You can find the cross browser function for obtaining the selection within a textarea here: Is there an Internet Explorer approved substitute for selectionStart and selectionEnd?

function transformTypedChar(charStr) {
    return /[a-g]/.test(charStr) ? charStr.toUpperCase() : charStr;
}

document.getElementById("your_input_id").onkeypress = function(evt) {
    var val = this.value;
    evt = evt || window.event;

    // Ensure we only handle printable keys, excluding enter and space
    var charCode = typeof evt.which == "number" ? evt.which : evt.keyCode;
    if (charCode && charCode > 32) {
        var keyChar = String.fromCharCode(charCode);

        // Transform typed character
        var mappedChar = transformTypedChar(keyChar);

        var start, end;
        if (typeof this.selectionStart == "number" && typeof this.selectionEnd == "number") {
            // Non-IE browsers and IE 9
            start = this.selectionStart;
            end = this.selectionEnd;
            this.value = val.slice(0, start) + mappedChar + val.slice(end);

            // Move the caret
            this.selectionStart = this.selectionEnd = start + 1;
        } else if (document.selection && document.selection.createRange) {
            // For IE up to version 8
            var selectionRange = document.selection.createRange();
            var textInputRange = this.createTextRange();
            var precedingRange = this.createTextRange();
            var bookmark = selectionRange.getBookmark();
            textInputRange.moveToBookmark(bookmark);
            precedingRange.setEndPoint("EndToStart", textInputRange);
            start = precedingRange.text.length;
            end = start + selectionRange.text.length;

            this.value = val.slice(0, start) + mappedChar + val.slice(end);
            start++;

            // Move the caret
            textInputRange = this.createTextRange();
            textInputRange.collapse(true);
            textInputRange.move("character", start - (this.value.slice(0, start).split("\r\n").length - 1));
            textInputRange.select();
        }

        return false;
    }
};
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
1

How about preventing default action and then triggering the keypress? Something like,

function onKeypressHandler(e)
{
    if ( condition )
    {
        e.preventDefault();
        // create new event object (you may clone current e)
        var ne = new jQuery.Event("keypress");
        ne.which = e.which - 32;
        $(e.target).trigger(ne);   // you may have to invoke with setTimeout
    }
}
VinayC
  • 47,395
  • 5
  • 59
  • 72
  • Won't work. You can't simulate the effects of a keypress in the browser. – Tim Down Oct 13 '10 at 11:40
  • Doh! This seemed promising until I saw Tim's comment. Nice idea anyway. :( – Peter Boughton Oct 13 '10 at 11:43
  • @Tim, thanks for info. BTW, example from jquery docs uses .keypress() to trigger the event. So what happens in such case - does only event handler get invoked w/o control receiving the character? – VinayC Oct 13 '10 at 11:49
  • @VinayC: Could you post the jQuery docs link? It may well be possible to simulate a keypress event so that it fires on a particular element (I haven't tried to do this) but it won't perform the usual browser action (such as making a character appear in an input). – Tim Down Oct 13 '10 at 12:04
  • @Tim - here's the link: http://api.jquery.com/keypress/. It uses keypress() to trigger the event handler. The example does not put any character in input but then it is not specifying any event object. – VinayC Oct 13 '10 at 12:23
  • @VinayC: Thanks. Their documentation could do with being clear about the fact that triggering a `keypress` event won't actually trigger the associated browser behaviour. All you get is the following vague disclaimer in the docs for `trigger()`: *"Although .trigger() simulates an event activation, complete with a synthesized event object, it does not perfectly replicate a naturally-occurring event."* – Tim Down Oct 13 '10 at 12:40
  • Apparently you can simulate a keypress.. check my answer below. (cant guarantee cross browser, but works in FF :) ) – Ravindra Sane Oct 13 '10 at 13:03
  • @Ravindra: Yes, it works in Firefox, but sadly only in Firefox. – Tim Down Oct 13 '10 at 15:07
1

You've got to see this.. I was pretty happy with myself after getting it to work..

You obviously would want to include sufficient criteria to avoid going into a loop here.

The code below returns false when condition evaluates to true, but it fires the same event with a different charCode which will not return false.

document.getElementById("input1").onkeypress = Handler;
function Handler(e)
{
    e = e || window.event;
    if ( e.charCode == 97 )
    {
        var evt = document.createEvent("KeyboardEvent");
        evt.initKeyEvent("keypress",true, true, window, false, false,false, false, 0, e.charCode -32);
        this.dispatchEvent(evt);
        return false;
    }
    return true;
}

you could use fireEvent in IE... I used http://help.dottoro.com/ljrinokx.php and https://developer.mozilla.org/en/DOM/event.initKeyEvent for reference

Ravindra Sane
  • 472
  • 3
  • 10
  • Interesting. This only works in Mozilla, however. Fortunately its scope seems limited: trying to trigger keyboard shortcuts such as Ctrl-B (for bookmarks), Ctrl-A (select all) doesn't work. I wasn't aware you could do this much though. – Tim Down Oct 13 '10 at 13:37
  • not exactly.. IE also supports firing events.. instead of dispatchEvent, you use fireEvent and to create the event, you use object.createEventObject. See http://help.dottoro.com/ljhlvomw.php. I am on Linux so cant help you with the code/testing. But this seems doable – Ravindra Sane Oct 13 '10 at 14:33
  • Seems the code for crossbrowser mouse event handling is already provided in that link. – Ravindra Sane Oct 13 '10 at 14:40
  • I've tested the IE version and while it fires a new event correctly, it doesn't perform the associated browser behaviour such as inserting a character in an input. – Tim Down Oct 13 '10 at 14:57
  • I also tested the original code in recent versions of Chrome, Safari and Opera, with the same result. This means that of the major browsers, your approach is only viable in Firefox. – Tim Down Oct 13 '10 at 15:02
  • Finally, in Firefox your code gets into an infinite loop: the `keypress` event you fire then causes the same event handler to be called again for ever, or at least until Firefox throws a "too much recursion" error. – Tim Down Oct 13 '10 at 15:06
  • For Chrome etc use initKeyboardEvent .. I did mention that there needs to be enough checking to avoid infinite loop in the second line of the answer itself. I wasn't writing production-quality code.. just giving the general idea. Accept it.. it is possible to fire another event with different parameters, if the current handler finds that the parameters dont meet your criteria. – Ravindra Sane Oct 14 '10 at 03:01
  • @Ravindra: the point is that the only browser where it is possible to make a character actually appear in the text box by firing a new keypress event is Firefox. – Tim Down Oct 14 '10 at 15:28
  • Maybe you can use this gist https://gist.github.com/termi/4654819 to make a keypress event. (Refer to https://stackoverflow.com/a/14561303/1191999) – Ram Sep 17 '17 at 16:03
0

Not really sure what you want but will this work?

$('#my_element').keyup(function(){ $(this).val().toUpperCase(); });

or use a sub string to get the last character pressed and do toUpperCase() on that?

(psst... you can use keydown or keypress too).

Thomas Clayson
  • 29,657
  • 26
  • 147
  • 224
-1

Can you use css?

<input type="text" style="text-transform: uppercase;" />
Paul Whelan
  • 16,574
  • 12
  • 50
  • 83
  • 2
    That doesn't change the actual input value, and more importantly doesn't apply conditionally. – Peter Boughton Oct 13 '10 at 11:29
  • 1
    Could you do a to uppercase when you are processing the submitted value? What do you mean by conditionally? – Paul Whelan Oct 13 '10 at 11:30
  • I've updated the question to clarify - I only want to (maybe) uppercase the current character, not the whole string - and if I uppercase it depends on an if condition (the precise check is unimportant to the question). – Peter Boughton Oct 13 '10 at 11:39
-1

Peter,

You might find some inspiration here:

http://www.maconstateit.net/tutorials/JSDHTML/JSDHTML15/jsdhtml15-05.htm

basically walks around the various ways to look at the keypress events and functions around that 'area'.

jim tollan
  • 22,305
  • 4
  • 49
  • 63