Skip to content

get the new added characters to an input by js

I know this seems a quite easy target. I have an input[type=text], and I want to detect the new added character(s) in it. The normal way is:

$selector.keypress(function(e) {
    //do sth here
    var newchar = String.fromCharCode(e.which);
});

But the above method not working properly for some browsers on android devices. Typing the android virtual keyboard will not fire the keypress.

Then I found the following method is better:

$selector.on('input', function(e){
    //do sth here
});

It works fine for android devices, and also, it can detect cut/paste.

Now the question is, is there a way to know the new added character(s) to the input? Do I need to do the complicated string comparison during inputing each time, i.e. compare the previous string and the new string in the input box? I said it’s complicated because you may not always type in char(s) at the end, you may insert some char(s) in the middle of the previous string. Think about this, the previous string in the input box is “abc”, the new string after pasting is “abcxabc”, how can we know the new pasted string is “abcx”, or “xabc”?

The method from keypress is quite simple:

String.fromCharCode(e.which);

So, is there similar way to do this by the on('input') method?


After reading Yeldar Kurmangaliyev’s answer, I dived into this issue for a while, and find this is really more complicated than my previous expectation. The key point here is that there’s a way to get the cursor position by calling: selectionEnd.

As Yeldar Kurmangaliyev mentioned, his answer can’t cover the situation:

it is not working is when you select text and paste another text with replacing the original one.

Based on his answer, I modified the getInputedString function as following:

    function getInputedString(prev, curr, selEnd) {
        if (selEnd === 0) {
          return "";
        }
        //note: substr(start,length) and substring(start,end) are different
        var preLen = prev.length;
        var curLen = curr.length;
        var index = (preLen > selEnd) ? selEnd : preLen;
        var subStrPrev;
        var subStrCurr;
        for(i=index; i > 0; i--){
            subStrPrev = prev.substr(0, i);
            subStrCurr = curr.substr(0, i);
            if (subStrCurr === subStrPrev) {
                var subInterval = selEnd - i;
                var interval = curLen - preLen;
                if (interval>subInterval) {
                    return curr.substring(i, selEnd+(interval-subInterval));
                }
                else{
                    return curr.substring(i, selEnd);
                }

            }
        }

        return curr.substring(0, selEnd);
    }

The code is quite self explanation. The core idea is, no matter what character(s) were added(type or paste), the new content MUST be ended at the cursor position.

There’s also one issue for my code, e.g. when the prev is abcabc|, you select them all, and paste abc, the return value from my code will be "". Actually, I think it’s reasonable, because for my scenario, I think this is just the same with delete the abc from previous abcabc|.

Also, I changed the on('input') event to on('keyup'), the reason is, for some android browsers, the this.selectionEnd will not work in a same way, e.g., the previous text is abc|, now I paste de and the current string will be abcde|, but depending on different browsers, the this.selectionEnd inside on('input') may be 3, or 5. i.e. some browsers will report the cursor position before adding the input, some will report the cursor position after adding the input.

Eventually, I found on('keyup') worked in the same way for all the browsers I tested.

The whole demo is as following:

DEMO ON JSFIDDLE

Working on the cross-browser compatibility is always difficult, especially when you need to consider the touch screen ones. Hope this can help someone, and have fun.

Answer

Important notes:

  • when a user types in a character, the cursor stands after it
  • when a user pastes the text, the cursor is also located after the pasted text

Assuming this, we can try to suggest the inputed pasted string.

For example, when we have a string abc and it becomes abcx|abc (| is a cursor) – we know that actually he pasted “abcx”, but not “xabc”.

How do this algorithmically? Lets assume that we have the previous input abc and the current input: abcx|abc (cursor is after x).

The new one is of length 7, while the previous one is of length 4. It means that a user inputed 4 characters. Just return these four characters 🙂

The only case when it is not working is when you select text and paste another text with replacing the original one. I am sure you will come up with a solution for it yoruself 🙂 Here is the working snippet:

function getInputedString(prev, curr, selEnd) {
  if (prev.length > curr.length) {
    console.log("User has removed  cut character(s)");
    return "";
  }

  var lengthOfPasted = curr.length - prev.length;
  if (curr.substr(0, selEnd - lengthOfPasted) + curr.substr(selEnd) === prev)
  {  
      return curr.substr(selEnd - lengthOfPasted, lengthOfPasted);
  } else {
      console.log("The user has replaced a selection :(");
      return "n\a";
  }
}

var prevText = "";

$("input").on('input', function() {
  var lastInput = getInputedString(prevText, this.value, this.selectionEnd);
  prevText = this.value;

  $("#result").text("Last input: " + lastInput);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<div id="result">Start inputing...</div>