1

I am posting this question with my answer so far but would like to invite other solutions as I am not 100% sure about mine.

It will:

  • Automatically place the dashes in the right place so the user only has to enter the digits.
  • Can be any size. You can set a maxlength attribute on your input and it will continue to apply dashes intil it runs out of space. It will default to 8 characters max
  • Allowsuser to delete digits without the need to delete the dashes too.

Why am I posting a this? I could not find the answer myself on StackOverflow and when you search this question on Google, it keeps returning a PHP answer for StackOverflow instead! There are even answers in there for Javascript. Hopefully this question can produce other solutions too!

How does it work?

This is designed to work with a real-time input.

  • It works out the maximum length
  • It captures the event and works out if the delete key was pressed
  • The 'regex' part is saying to replace ever 2nd character with itself plus a dash.
  • The next line first replaces anything that's not a number, then uses the regex to inject dashes and finally the string is sliced to remove any trailing slash

You would apply this function to your onkeyup or onpaste events, passing 'this' in.

function checkSortCode(el,ev){
      var len = el.maxLength || 8;
            ev = ev || window.event;
            if(ev.keyCode == 8 && el.value.slice(-1) == "-"){
                el.value = el.value.slice(0,-1);
            } else {
                var regex = new RegExp("(\\S{" + (2 - 1) + "}\\S)", "g");
                el.value = el.value.replace(/[^0-9]/g,"").replace(regex,("$1"+"-")).slice(0,len);
            }
        }
.sortcode::placeholder{color:#eeeeee;}
body{font-family:arial,sans-serif;font-size:1.4em;}
input{font-size:1.4em;}
<label>Sort Code</label><br>
<input type="text" name="sortcode" onkeyup="checkSortCode(this,event)" onpaste="checkSortCode(this,event)" class="sortcode" size="8" maxlength="8" placeholder="00-00-00" />

Ideally, I wanted it to show the 00-00-00 format all the time and then the user would fill it in but have padded zeros where they hadn't. That's not easy as the cursor wants to go to the end of the input.

Watts Epherson
  • 692
  • 5
  • 9
  • 1
    just a suggestion, you can have 3 input fields instead of single input. That way you can show 00 in placeholder for not filled inputs. and if your gives single digit and moves to second input, you can always pad a 0 in the front of first input. -- just a thought :D – Anu Feb 14 '23 at 12:43
  • Thanks, yes I was hoping someone could show that solution so it was smart enough to know to move to the next input field. – Watts Epherson Feb 14 '23 at 14:39

1 Answers1

0

What you're looking for is called Input Masking. You can implement it yourself but I would recommend using a library to separate the actual input value and the mask.

Here an implementation using native js, you'll notice it's a bit janky.

<html>
<body>

    <input id="input">

    <script>
        const pattern = '00-00-00-00'
        const patternRegex = /^[0-9]{2}\-[0-9]{2}\-[0-9]{2}\-[0-9]{2}$/
        const separator = '-'

        /* returns current value completed by the pattern (filled with 0) */
        const fill = value => {
            return `${value}${pattern.substring(value.length)}`
        }

        /* format the input on keyup */
        const format = event => {
        
            /* only format the input at cursor position (to ignore filled pattern) */
            const position = event.target.selectionStart
            const value = event.target.value.substring(0, position)

            /* rollback invalid inputs */
            if (!patternRegex.test(fill(value))) {
                event.target.value = event.target.value.substring(0, position - 1)
                return
            }

            /* change target valuer to include pattern and restore carret position */
            event.target.value = fill(value)
            const newPosition = event.target.value[position] === separator ? position + 1 : position
            event.target.setSelectionRange(newPosition, newPosition)
        }

        const input = document.getElementById('input')
        input.addEventListener('keyup', format)
    </script>

</body>
</html>

You can check some other implementation here : https://css-tricks.com/input-masking/

The reason why it's janky is because we format the input after a change occured. When using a library (or React), you can control the input value before it's rendered.

Kevin Gilbert
  • 903
  • 8
  • 21
  • 1
    Thanks Kevin. I can see what you mean! It's a great example of having the input pre-rended. Deleting causes some issues but I guess the user probably isn't messing too much inside that field! :) – Watts Epherson Feb 14 '23 at 14:41