javascriptevent-handlingdom-eventssanitizationcursor-position

How can I validate field input against a regex when pasting?


I want to validate an input field with regex (replacing all vowels). The input should always be matched against that regex, regardles if typed into or pasted into that field.

I know how to deal with the typing part, but I need help for copy - paste. All vowels should be skipped (e.g.: "abcde" copy --> "bcd" paste)

    $('document').ready(function(){
      var pattern = /[aeiou]/ig;

      $("#input").on("keypress keydown", function (e) {
        if (e.key.match(pattern) !== null) {
          e.preventDefault();
        }
      });
      
      $("#input").on("paste", function (e) {
        var text = e.originalEvent.clipboardData.getData('Text');
        e.preventDefault();

        //TODO... replace all vowels
        console.log(text);
      });
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='text' id='input'/>

At first i tried to replace them and just set the value with $('#input').val($('#input').val() + copiedValue), but this only works if the cursor is at the end of the input.


Solution

  • A one in all solution subscribes to the input event. In addition to the sanitizing tasks the handler has to take care of reestablishing the input fields exact or most expected cursor/caret position in order to not break the user experience ...

    function getSanitizedValue(value) {
      return value.replace(/[aeiou]/ig, '');
    }
    
    function handleInput(evt) {
      evt.preventDefault();
    
      const elmNode = evt.currentTarget;
    
      const currentValue = elmNode.value;
      const sanitizedValue = getSanitizedValue(currentValue);
    
      if (currentValue !== sanitizedValue) {
        const diff = sanitizedValue.length - currentValue.length;
        const { selectionStart, selectionEnd } = elmNode;
    
        elmNode.value = sanitizedValue;
    
        elmNode.selectionStart =
          (selectionStart + diff > 0) ? selectionStart + diff : selectionStart;
        elmNode.selectionEnd =
          (selectionEnd + diff > 0) ? selectionEnd + diff : selectionEnd;
      }
    }
    
    $('document')
      .ready(() => {
    
        $("#input").on("input", handleInput);
      });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type='text' id='input'/>