javascript input mask currency

Viewed 22976

i got this error on input.value and input.length:

scripts.js:151 Uncaught TypeError: Cannot read property 'length' of undefined

I'm trying to apply a currency mask to an input, but I'm getting errors

function formatNumber(n) {
  // format number 1000000 to 1,234,567
  return n.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}

function formatCurrency(input, blur) {
  // appends $ to value, validates decimal side
  // and puts cursor back in right position.

  // get input value
  var input_val = input.value;
  console.log(input)
  // don't validate empty input
  if (input_val === "") { return; }

  // original length
  var original_len = input_val.length;

  // initial caret position 
  var caret_pos = input.getAttribute("selectionStart");

  // check for decimal
  if (input_val.indexOf(".") >= 0) {

    // get position of first decimal
    // this prevents multiple decimals from
    // being entered
    var decimal_pos = input_val.indexOf(".");

    // split number by decimal point
    var left_side = input_val.substring(0, decimal_pos);
    var right_side = input_val.substring(decimal_pos);

    // add commas to left side of number
    left_side = formatNumber(left_side);

    // validate right side
    right_side = formatNumber(right_side);

    // On blur make sure 2 numbers after decimal
    if (blur === "blur") {
      right_side += "00";
    }

    // Limit decimal to only 2 digits
    right_side = right_side.substring(0, 2);

    // join number by .
    input_val = "$" + left_side + "." + right_side;

  } else {
    // no decimal entered
    // add commas to number
    // remove all non-digits
    input_val = formatNumber(input_val);
    input_val = "$" + input_val;

    // final formatting
    if (blur === "blur") {
      input_val += ".00";
    }
  }

  // send updated string to input
  input.value(input_val);

  // put caret back in the right position
  var updated_len = input_val.length;
  caret_pos = updated_len - original_len + caret_pos;
  input[0].setSelectionRange(caret_pos, caret_pos);
}
(function initalize() {
  // IIFE method
  let input = document.querySelector("input[data-type='currency']")
  input.addEventListener("keyup", () => {
    formatCurrency(this);
  })
  input.addEventListener("keyup", () => {
    formatCurrency(this, "blur");
  })

})();
.textfield_label {
  font-family: "Poppins";
  color: #a3a3a3;
  margin-bottom: 10px;
  text-transform: uppercase;
  letter-spacing: 1.5px;
}
.material-textfield {
  font-family: "Poppins";
  position: relative;
  margin-bottom: 30px;
  color: #a3a3a3;
}

.material-textfield label {
  font-family: "Poppins";
  position: absolute;
  font-size: 1.2rem;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  background: #fff;
  border-radius: 5px;
  color: gray;
  padding: 0 0.3rem;
  margin: 0 0.5rem;
  transition: 0.1s ease-out;
  transform-origin: left top;
  pointer-events: none;
}
.material-textfield input {
  font-family: "Poppins";
  width: 100%;
  font-size: 1rem;
  outline: none;
  border: 1px solid #ffb24f;
  border-radius: 5px;
  background: #00416a;
  padding: 1rem 0.7rem;
  color: #ffb24f;
  transition: 0.1s ease-out;
}
.material-textfield::before {
  background: none;
  border: 1px solid #ffb24f;
  content: "";
  display: block;
  position: absolute;
  top: 2px;
  left: 2px;
  right: 2px;
  bottom: 2px;
  pointer-events: none;
}
.material-textfield input:focus {
  border-color: #ebb76e;
}
.material-textfield input:focus + label {
  color: #ebb76e;
  top: 0;
  transform: translateY(-50%) scale(0.9);
  background: linear-gradient(
    180deg,
    rgba(235, 235, 235, 1) 0%,
    rgba(235, 235, 235, 1) 50%,
    rgba(255, 255, 255, 1) 50%,
    rgba(255, 255, 255, 1) 100%
  );
}
.material-textfield input::placeholder{
  color:#ebb76e;
}
          <div class="input_renda">
            <h3 class="textfield_label">
              value
            </h3>
            <div class="material-textfield">
              <input type="text" name="currency-field" id="currency-field" pattern="^\$\d{1,3}(,\d{3})*(\.\d+)?$"
                value="" data-type="currency" placeholder=" $1,000,000.00" />
            </div>
          </div>

but I don't know why the input. value is not working, why do I get the element with the query selector If anyone can help me with this I'm happy

4 Answers

You can use the following function to mask currencies numbers:

export const moneyMask = (value: string) => {
  value = value.replace('.', '').replace(',', '').replace(/\D/g, '')

  const options = { minimumFractionDigits: 2 }
  const result = new Intl.NumberFormat('pt-BR', options).format(
    parseFloat(value) / 100
  )

  console.log(result)

  return 'R$ ' + result
}

This bug is caused by the improper use of the this keyword inside:

  let input = document.querySelector("input[data-type='currency']")
  input.addEventListener("keyup", () => {
    formatCurrency(this);
  })

Here you're trying to pass the input object as a parameter to the formatCurrency function but this doesn't refer to it. To get a reference you need to use the special e parameter for the callback function and access it using e.target which holds a reference to the object that caused the event.

e.g.

  let input = document.querySelector("input[data-type='currency']")
  input.addEventListener("keyup", (e) => {
    formatCurrency(e.target);
  })

Furthermore, later on in your script you're trying to assign a value back to the input object by:

input.value(input_val);

With the above you're actually trying to call a function named value() - to change the .value property of your object you need to do it like this:

input.value=input_val;

Simple demo of usage of the built-in currency mode of .toLocaleString().

currencyfield.addEventListener("input", format, false)

function format (){
  let val = +currencyfield.value;
  document.querySelector(".textfield_label").textContent =  val.toLocaleString('fullwide', {maximumFractionDigits:2, style:'currency', currency:'USD', useGrouping:true}) 

}
<div class="input_renda">
   <h3 class="textfield_label">$0.00</h3>
   <div class="material-textfield">
     <input type="text" name="currency-field" id="currencyfield" value="" data-type="currency" placeholder=" $1,000,000.00" />
   </div>
</div>

       
   let input = document.getElementsByClassName("productPrice");

console.log(input.length);

    for (var i = 0; i < input.length; i++) {
        input[i].addEventListener("keyup", (e) => { formatCurrency(e.target); });
        input[i].addEventListener("keypress", (e) => { formatCurrency(e.target); });
    }

    function formatNumber(n) {
        // format number 1234567,89 to 1 234 567,89
        return n.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, " ")
    }
    function formatCurrency(input) {
        var input_val = input.value;
        if (input_val === "") { return; }
        var original_len = input_val.length;
        var caret_pos = input.selectionStart;
        if (input_val.indexOf(",") >= 0) {
            var decimal_pos = input_val.indexOf(",");
            var left_side = input_val.substring(0, decimal_pos);
            var right_side = input_val.substring(decimal_pos);
            left_side = formatNumber(left_side);
            right_side = formatNumber(right_side);
            right_side = right_side.substring(0, 2);
            input_val = left_side + "," + right_side;

        } else {
            input_val = formatNumber(input_val);
        }
        input.value = input_val;
        var updated_len = input_val.length;
        caret_pos = updated_len - original_len + caret_pos;
        input.setSelectionRange(caret_pos, caret_pos);
    }
 <input type="text" class="productPrice">
    <br>
    <br><input type="text" class="productPrice">
    <br>
    <br><input type="text" class="productPrice">
    <br>
    <br><input type="text" class="productPrice">
    <br>
    <br>

Related