Changing a user input on keypress

I have a section on my website where users enter a sting of numbers. I want those numbers to automatically display in groups of four as the user is inputting the information. For example:
11111111. ----> 1111 1111

Any help would be great. Thank you!!

@anneynorton7 Hi There, you should look at the onKeyPress() function.

Basically as a key is pressed in an input box this function is called. Now in order to adjust the input to include spaces then you need to assess that the input characters are numerical or a space and are as defined in your request. There are several ways to validate the characters and, if a space is not present at the appropriate points, rewrite the value of the input text box. Now this could be bit confusing depending on the timing of character entry. Here is a simple example of an onKeyPress() function that would do what you seem to be looking for:

// This code assumes that the numbers are being entered into an element called $w(‘#input1’).

$w.onReady(() => {
    // Initialize number input string formatter handler
    $w('#input1').onKeyPress(fourDigitFormatter);
});

// 
function fourDigitFormatter(event) {
    // Get the key value that was pressed
    let keyPressed = event.key;
    let validChars = /[\d ]/;
    // Make sure that the key is a number value or a space
    if (validChars.test(keyPressed)) {
        // We have a correct value
        let validFormat = /\d{1,4}/;
        let inputValue = $w('#input1').value;
        let numberList = inputValue.split(' ');
        // Make sure that the input value meets the formatting requirement
        // Each number string in the list must be digits and of length 4 except the
        // last string which can be less than 4
        let numberListLength = numberList.length - 1;
        for (var i = 0; i <= numberListLength && keyPressed !== ' '; i++) {
            let testString = numberList[i];
                if (testString === '' || validFormat.test(testString)) {
                    // The numberList entry is valid
                    if (i === numberListLength) {
                        // Processing the last number in the list
                        if (testString.length < 4) {
                            numberList[i] = testString+keyPressed;
                        } else {
                            numberList.push(keyPressed);
                        }
                    } else {
                        // Processing intermediate entry list
                        if (testString.length !== 4) {
                            console.log("Bad string format - length!");
                            break;
                        }
                    }
                } else {
                    // String doesn't meet formatting requirement
                    console.log("Bad string format - non-numerical!")
                }
            }
 
            let newValue = numberList.join(' ');
            $w('#input1').value = newValue;
 
        } else {
            console.log('bad char pressed! - '+keyPressed);
        }
    }
    


Hi Steve,

I tried to use this code but here when you type fast then it will look something like this. Is there any other way to fix this?

I tried many times doing a good auto formatting while typing in WIX, but WIX does not have keyup as vanilla JavaScript, it was never as good as it should, especially typing fast.

What I use now is the method .onBlur() that formats anything I need as I change the focus out of the input.

Look at the top left input and how it works:

$w("#inputMoney").onBlur(({ target }) => {
    target.value = formatToCurrency(target.value)
})

function formatToCurrency(value) {
    typeof value === "number" ?
        (value = value.toString()) :
        (value = value.replace(/,/g, "."))
    return new Intl.NumberFormat("pt-BR", {
        style: "currency",
        currency: "BRL",
    }).format(value)
}

Try this currency formatter.

I have also done conditional grouping when formatting phone # fields. But I did it using the onChange event. So the field was updated once the user left the field. Do you require it in realtime or can you do it post entry. Also how many groups of 4 are you expecting/limiting it to. My method seems a little simpler. I use Regex to convert the number into groups. I also evaluate the whole string at once, rather than in a loop. Initially I add the keypress to the existing value in the field. Then I strip out all spaces and non numeric values from the string. Then I build the regex, run it and recreate the string with the spaces between every group of four.

I am wondering if the strange behavior of the numbers not going in numerically in order, is due to the processing of the onKeypress event not completing before another key is pressed, causing the skewing of the numbers.

Here is the code:
------------------------ begin of code --------------------------

function fourDigitFormatter(event) { 

const numblocks = 4;  // group numbers into blocks of 4 
let numbr = ($w('#input1').value + event.key).split(' ').join('').replace(/\D/g,''); // remove all non digits and spaces 
let len = numbr.length;  // get total length of numbers with no spaces 
let regvar = ""; 
    let newnumber = ""; 

if (len >0 ) { 
    let cntr = Math.floor(len/numblocks);  // get number of blocks of 4 
    if (cntr >0) {  
        // build a regex expression string, to add in each block of 4 
        for(let i =1;i <=cntr;i++) { 
	    regvar = regvar + "(\\d{" + numblocks + "})"; 
        } 
            // compute remainder value, last group (<4) 
        len = len - (cntr * numblocks); 
 	    } 
    if (len >0) {  
        // take care the last group that would be < 4 numbers 
        regvar = regvar + '(\\d{' + len + '})';	 
    } 
        let regex = new RegExp('^'+regvar+'$');  // build regex from string 
    let numarr = numbr.match(regex);  // convert number into array of groups of 4 
        // rebuild new number into groups of 4 with spaces inbetween 
    if (numarr.length >0) { 
        if (cntr >0) {  
	    for(let i=1;i<=cntr;i++) { 
	        newnumber = newnumber + numarr[i] + ' '; 
	    } 
        } 
        if (len >0) {  
	    newnumber = newnumber + numarr[cntr+1]; 
        } 
    } 
} 
    $w('#input1').value = newnumber; 
} 

------------------------ end of code ----------------------------

1 Like

Hi Paul,
I am exactly looking for what you have done but there is any issue when you press backspace. I can’t delete the numbers using backspace one by one.

@pekrzyz
Hey Paul → try to use CODE in CODE-BLOCKS.

Your code will be shown nice formatted and good readable.
Take a look onto the given post-options.

Yes, I can see that that is an issue. You can trap for a backspace before you scrub the numbers. Check the key value, and if it is a backspace, then decrement the number string in the field by 1 (ensure in your check that you don’t go negative). If it is not a backspace, then just continue processing like you normally would.

if (event.key == 8) {
// we have a backspace key, process it separately
let len= $w(“#input1”).value.length;
if (len >0) {
if (len == 1) {
$w(“#input1”).value = “”;
} else {
$w(“#input1”).value = $w(“#input1”).value.slice(0, len-1);
}
}
} else {

… continue with code here
}

Something like this. I haven’t tested this, so you will need to. I am assuming that $w(“#input1”).value, will return as a string. If not, you will have to cast it as a string, before the .slice will work. I am also hoping that Wix, set the backspace key to be 8, as it is on other sites. (Just quickly googled backpace and javascript).

In my original code, I did not use event.key, since I evaluated the input once the user entered all the numbers. I used the onChange() event, not the onKeyPress() event. When I am adding the event.key to the number string, I am assuming I am getting a string back. I have not been able to test this. If you get an error there, you are most likely getting a number from event.key. Therefore before adding it to the $w(“#input1”).value, you may need to convert the event.key to a string. Something like, event.key.toString() .

1 Like

@russian-dima Totally agree with you. Still working on getting comfortable with Wix interface on forums and how to use its features. I agree it looks a lot better.

1 Like

@pekrzyz
Sorry forgot to mention, that the usage of the code-block on mobile is not available. So if you generate your posts from mobile you can’t use code-block/tags.

It is working. The only thing I changed is replaced 8 with ‘Backspace’ .

Thank you so much Paul!

I made a few last second changes to the testing of the backspace part (about an hour ago). just make sure you have the lastest change. I changed the test to check if the length is 1, else I decrement the length by one. Did is solve the issue with the numbers not going in order? I suspect this solution executes faster, since it isn’t doing as much work as before.

It worked well but there are still some minor issues such as when you place your cursor in between the numbers the press backspace or delete, it behaves strange. Also if your try to select few numbers and delete them, then you have to press the backspace/delete key twice.

@skarmacharyawd There is a discussion back in 2018 talking about using a debounce with onKeyPress.

Here’s the article. I have not used that method. The example is Corvid stuff, and I don’t know if it is deprecated.

The example, uses a time delay. I looked at the code, but the event uses 2 paramters passed in. And today, there is only one parameter passed into the onKeyPress function. I think the second parameter isn’t used anymore.

I wonder what would happen if you created a small time delay after the event fires.
eg : setTimeout( () => {} , 10);
Put this as the first line, and see what happens to your stuff. You can try and increase the 10 (milliseconds), but don’t go over 500 (1/2 second). In those examples, they don’t even use the event.key. They just read the value from the input field.

That means you could remove the whole test for the backspace, remove the if block and put the rest of the code after the else, outside of the if block.

Try the following change:

  • remove the if section that tests for the backspace.

  • add the following line above (const numblocks = 4)
    setTimeout( () => {console.log(“Waiting for 10 milliseconds.”)} , 10);

  • modify the line (remove the event.key)
    let numbr = ($w(‘#input1’).value).split(’ ‘).join(’‘).replace(/\D/g,’'); // remove all

@pekrzyz yes, using debounce timer is still used in many applications, you can see it in my code. With WIX we don’t have the input formatting as regular HTML input elements, that’s why I, personally, prefer using the .onBlur() method.

The behavior @skarmacharyawd is describing it is almost impossible to fix in WIX, as far as I am aware.

I tried but it didn’t work. That’s okay I will leave as it is for now. Let’s see if I get any solutions later. Thanks for your help Paul.

@bwprado Thanks Bruno for your help. Really appreciate it.

1 Like

You can also use the onInput() event handler which will trigger when the text input element is updated with the new value.

@skarmacharyawd this is an example with a simple code.

$w.onReady(async () => {
    let debounceTimer
    $w('#inputNumbers').onInput(({ target }) => {
        clearTimeout(debounceTimer)
        debounceTimer = setTimeout(() => target.value = addSpacing(target.value), 200)
    })

    $w('#inputBlur').onFocus(({ target }) => target.value = clearFormatting(target.value))
    
    $w('#inputBlur').onBlur(({ target }) => target.value = addSpacing(target.value))
})

const clearFormatting = value => value.replace(/[^0-9]/g, '')
const addSpacing = value => value.replace(/(\d{4})/g, '$1 ')

Try Formatting, click here!

@bwprado I used the onBlur() method as there were issues when I used keyPress and on Input method. They were not smooth.

Thanks a lot Bruno!