'How to sanitize (digits and hyphen only) and auto-format (hyphen and leading zeros insertion) a text input value while typing and/or pasting text?

There are similar questions on SO, but mine is a bit unique. I want to limit an input text field to 9 characters in length (currently solved with maxlength attribute), only allow typing in numeric values and the hyphen character. Sort of handled with this code returning "True":

/^\d*\-?\d*$/.test(value)

Where I'm stuck is, I want the input text field to auto-format the value as the user types in the format:

12345-123

Where it's 5 digits (may have leading zeros or not depending on how user inputs it), followed by a hyphen, then always 3 digits. I'd like it to pad the first 5 with zeros if user enters something like "123-495" manually, so it would become "00123-495".

I'm not sure how to add in the auto-zero padding, or placement of the hyphen automatically.

Not opposed to using jQuery, but would prefer vanilla.

EDIT: Thought it might be useful to add. This is for an access card number entry box. So value will always be a positive number, and will always have 3 digits after the single hyphen. The card number will always be 5-digits in length, but again, may be padded with zeros to make it that length. Ideal output should always be "xxxxx-xxx".

EDIT 2: This seems to work, but there's an issue where user can enter non-numeric characters at first and after the 1st entry, only then does it clear it out. It also doesn't seem to let me hit backspace past the hyphen... Is there a way to prevent it from allowing alpha characters completely?

// Restricts input for the given textbox to the given inputFilter function.
function setInputFilter(textbox, inputFilter) {
    ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function(event) {
        textbox.addEventListener(event, function() {
            if (inputFilter(this.value)) {
                // Current value
                new_val = '';
                if (this.value.includes('-') && this.value.slice(this.value.indexOf('-')).length == 4) {
                    console.log("Value not hyphenated yet");
                    pad_needed = 5 - this.value.indexOf('-');
                    console.log('Pad needed: ' + pad_needed);

                    new_val = this.value.padStart(9, '0');
                    this.value = new_val;
                } else if (this.value.length >= 5 && this.value.includes('-') && this.value.slice(this.value.indexOf('-')).length == 4) {
                    if (this.value.slice(5, 1) == '-') {
                        // Already a hyphen added, just add rest of numbers
                        new_val = this.value.slice(0, 6) + this.value.slice(6);
                    } else {
                        // Needs hyphen added
                        new_val = this.value.slice(0, 5) + '-' + this.value.slice(6);
                    }
                    this.value = new_val;
                } else if (this.value.length >= 5 && !this.value.includes('-')) {
                    // Needs hyphen added
                    new_val = this.value.slice(0, 5) + '-' + this.value.slice(6);
                    this.value = new_val;
                }

                this.oldValue = this.value;
                this.oldSelectionStart = this.selectionStart;
                this.oldSelectionEnd = this.selectionEnd;
            } else if (this.hasOwnProperty("oldValue")) {
                this.value = this.oldValue;
                this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
            } else {
                this.value = "";
            }
        });
    });
}

setInputFilter(document.getElementById("card-number"), function(value) {
    return /^\d*\-?\d*$/.test(value); // Allow digits and '-' only
});


Solution 1:[1]

function getSanitizedInputValue(value) {
  value = value
    .trim()
    .replace(/^[-]+/, '')
    .replace(/[-]+/, '-');

  let [
    first,
    ...rest
  ] = (value.match(/[-\d]+/g) ?? [])
    .join('')
    .split('-')

  let joiner = '';

  if (first.length >= 6) {
    joiner = '-';

    rest.unshift(first.slice(5));
    first = first.slice(0, 5);

  } else if (rest.length >= 1) {
    joiner = '-';

    first = first.padStart(5, '0');
  }
  return [
    first,
    rest.join(''),
  ]
  .join(joiner)
  .slice(0,9);
}

function handleInput({ currentTarget: control }) {
  const { value: recentValue, selectionStart, selectionEnd } = control;
  const regXHasHyphen = /-/;

  const sanitizedValue = getSanitizedInputValue(recentValue);

  const sanitizedLength = sanitizedValue.length;
  const recentLength = recentValue.length;

  const positionDelta = ( 
    (recentLength <= 5) &&
    (sanitizedLength >= 6) &&
    (sanitizedLength - recentLength)
  ) || (
    !regXHasHyphen.test(recentValue) &&
    regXHasHyphen.test(sanitizedValue) &&
    1
  ) || 0;

  control.value = sanitizedValue;

  control.selectionStart =
    Math.min(sanitizedLength, (selectionStart + positionDelta));
  control.selectionEnd =
    Math.min(sanitizedLength, (selectionEnd + positionDelta));
}
document
  .querySelector('[type="text"]')
  .addEventListener('input', handleInput);
<input type="text" maxlength="9" />

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1