0

I try to create an MS-Dos emulation window with jquery and ajax. It works well but when I type a word or press enter, the script is one character too late on display (no character displayed on first press then, for each next keydown, it shows the previous character typed by the user instead of the current one).

Here is a simplified version of my script:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="css/terminal.css">
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() 
            {       
                //set cursor position
                $('#cursor').css('left','0px');

                //focus on textfield
                $('#field').focus();

                //On key down...
                $('#field').keydown(function(event) 
                {
                    request=$(this).val();
                    count=$(this).val().length;

                    //Display chars as they come
                    $("#instruct").html(request);

                    // If "enter" is pressed
                    if (event.which == 13) 
                    {
                        event.preventDefault();

                        //clean user entry
                        request=request.replace(/\n/g, "");

                        // Process user entry
                        $.ajax({
                              type: "POST",
                              url: "scripts/ajax/terminal.php",
                              data: {command:request},
                              success: function(response) 
                              {
                                // if user typed something before pressing "enter"
                                if(response !='')
                                {
                                    // update list by logging the previous entry, showing system response and clearing the textarea
                                    $('#log').append("<li class='user'>--- &gt;<span>"+request+"</span></li>");
                                    $('#log').append("<li class='sys' >SYS &gt;<span>"+response+"</span></li>");
                                    $('#field').val('');
                                }
                              }
                        }); 
                    }
                });             
            });
        </script>   
    </head>

    <body>
        <ul id="log">
            <li class='sys'>Sys &gt;<span>Identification required</span></li>
        </ul>

        <ul id="realtime">
            <li id="live-space">
                <span class="user-line">--- &gt;</span>
                <textarea type="text" id="field"></textarea>
                <div>
                    <span id="instruct"></span><div id="cursor">_</div>
                </div>
            </li>
        </ul>
    </body>
</html>

I'm sure "keyDown" is fired when it should because when I change this

$("#instruct").html(request);

into this

$("#instruct").html(request+'x');

...the "x" will show up directly after the first keydown while the "request" content will be delayed to the next keydown (for instance, if I type "a", I will only see "x" and when I type "b" immediatly after, I see "ax", etc...).

So it's not about firing the keydown but about picking the right character at the right time.

What do I do wrong?

Thank you.

Mat
  • 202,337
  • 40
  • 393
  • 406
Baylock
  • 1,246
  • 4
  • 25
  • 49
  • I tried keydown, keypress and keyup. Each one alone (they share the same issue) or any combination of those together. If I use the three of them to fire the same function, I have something that looks almost right.But some other bells and whistles (like moving the cursor) are going crazy (the cursor will move 3 times instead of one each time I press the arrow key etc...). The thing is, it should work with keyDown only, right? – Baylock Dec 25 '12 at 14:16
  • 1
    @Baylock: It shouldn't, and I describe why in my answer: you're too early. – Amadan Dec 25 '12 at 14:17
  • [key event magic](http://jsfiddle.net/mayankcpdixit/5nkr2bnd/) – mayankcpdixit Sep 08 '15 at 07:27

2 Answers2

3

Working demo

The field's value isn't being updated before you catch it, just append the char of key pressed, but String.fromCharCode(event.which) returns the uppercase char if you use keydown so use keypress

$('#field').on('keypress',function(event){
    request=$(this).val() + String.fromCharCode(event.which);
    ...

or you could use

$('#field').on('keyup',function(event){
    request=$(this).val(); // value is ready by now
    ...

but the feedback appears slower because you're waiting for the key to be released

Popnoodles
  • 28,090
  • 2
  • 45
  • 53
  • This is almost ok, thank you! One last issue though: the current character is uppercase. When I type a new character, the previous one becomes lower case (and the new one is uppercase etc...) – Baylock Dec 25 '12 at 14:22
  • Almost perfect with keypress (keyup is not responsive enough for realtime typewriting). No uppercase issue anymore and chars are behaving the way I expect them to BUT I still need to hit "enter" twice to see the sys response. I believe "fromcharcode" does not consider line break, does it? – Baylock Dec 25 '12 at 14:45
  • I can't see why you need to press enter twice – Popnoodles Dec 25 '12 at 15:15
  • I come back on this again as keypress is not good enough... I need to use the arrows of the keyboard (along with shift, etc..) and keypress only deals with printable characters. So I'm back to square "two" which is: how to use keydown instead of keypress without having the uppercase characters issue? PS: I tried the change() event handler but can't get anywhere. My script just freezes when I do so. PPS: I read a little bit about keydown() and it seems that it's not case sensitive, only uppercase so I believe I'm in a pickle. How do people usually do to achieve realtime writing with javascript? – Baylock Dec 25 '12 at 15:53
  • Use `event.charCode` instead of `event.which` – Andre Figueiredo Apr 01 '14 at 18:25
  • plus, I don't see why `$(this).val()` is better than vanilla JS `this.value` in this case... – Andre Figueiredo Apr 01 '14 at 18:27
  • `$(this).val()` is only "better" than vanilla JS `this.value` in this case because the human is doing jQuery. – Popnoodles Apr 02 '14 at 00:58
2

It's about the timing. At the time of keydown event, the control hasn't yet received the character - in fact, it's not even sure if it will receive it (since you can block keydown or keypress, and it is only added after it). So when you read the contents of your control, you're doing it too early - it's like trying to eat a hamburger when you decide to go to McDonalds, instead after you go and pay for it.

You can read more about keyboard events here, and in a quite a bit more detail in the spec here.

The solution: this is not what keydown is meant for. Try and see if change event works better for you. EDIT: I meant, catch the characters as they come with the change event. Catch the Enter key with the keydown as you were doing.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thanks for your input. That's what I had in mind but wha would be the solution to get this to work? TYVM – Baylock Dec 25 '12 at 14:13
  • I believe I did write an explanation of the issue. If I dropped a small version of the script it's because I wasn't sure it was a theoretical issue. It could have been a lack of logic in my javascript instructions. – Baylock Dec 25 '12 at 14:25
  • 1
    Aaaaand I can't read. Sorry, must've been later than I thought. Apologies. – Amadan Dec 25 '12 at 14:26