Search code examples
reactjsnumber-formatting

How to edit second digit of a 7 figure number in Javascript?


I am working on a ReactJS application and have an input which can have figures in the millions. I am using the following function to format the number with commas:

  return Number(number).toLocaleString('en-GB', { minimumFractionDigits: 2 });

On the onChange prop for the input I have the following to stop the cursor jumping to the end:

        const caret = e.target.selectionStart;
        const element = e.target;
        window.requestAnimationFrame(() => {
          element.selectionStart = caret;
          element.selectionEnd = caret;
        });

When trying to change the second figure in one million number '1,000,000', deleting the second zero results in the cursor appearing after the second figure instead of the first figure. How can I make sure the cursor appears in the expected position when deleting digits in a seven-figure number?

Appreciate any advice


Solution

  •   const [value, setValue] = useState("1000000");
      const displayValue = Number(value).toLocaleString("en-GB", {
        minimumFractionDigits: 2,
        maximumFractionDigits: 2
      });
    
      const handleChange = e => {
        const oldLen = displayValue.length;
        const element = e.target;
        let rawString = element.value;
        let caret = element.selectionStart;
    
        // block removing decimal point
        if (caret === element.value.length - 2 && element.value.length < oldLen) {
          rawString = rawString.slice(0, caret) + "." + rawString.slice(caret);
        }
    
        // block removing commas
        if (displayValue.charAt(caret) === "," && rawString.charAt(caret) !== ",") {
          rawString = rawString.slice(0, caret) + "," + rawString.slice(caret);
        }
    
        const firstCommaAt = rawString.search(/,/g);
        const newLen = rawString.length;
    
        // caret position correction
        if (newLen > oldLen) {
          if (
            (firstCommaAt === 3 && caret > firstCommaAt) ||
            firstCommaAt === 4 ||
            newLen === 7
          ) {
            caret += 1;
          }
        }
        if (newLen < oldLen) {
          if (firstCommaAt === 1 && caret > firstCommaAt) {
            caret -= 1;
          }
        }
    
        // store parseable value
        setValue(rawString.replace(/,/g, ""));
        window.requestAnimationFrame(() => {
          element.selectionStart = caret;
          element.selectionEnd = caret;
        });
      };
    
      return (
        <input value={displayValue} onChange={e => handleChange(e)} />
      )
    

    working example: https://codesandbox.io/s/input-with-usestate-z48s2