Basj
Basj

Reputation: 46493

Why does adding a decimal point to an input clear the input?

I'm doing a small number keypad in Javascript, and I can't add a decimal point . to an <input type="number"> by doing:

document.getElementById('2').onclick = () => document.getElementById('input').value += '2';
document.getElementById('.').onclick = () => document.getElementById('input').value += '.';
<input id="input" type="number" value="3"></input>
<div id="2">click to add 2</div>
<div id=".">click to add .</div>

The specified value "3." cannot be parsed, or is out of range.

But on the other hand, we can manually enter the decimal point with the keyboard when the input has focus.

Full example:

var target = document.querySelector('#input');
document.querySelectorAll('.calcbutton').forEach(el => el.addEventListener("click", evt => { target.value += evt.target.innerHTML; }));
document.querySelector('.calcpoint').onclick = evt => { if (!target.value.includes('.')) target.value += '.'; };
<input id="input" type="number"></input>
<div id="calc">
<table>
<tr><td class="calcbutton">7</td><td class="calcbutton">8</td><td class="calcbutton">9</td></tr>    
<tr><td class="calcbutton">4</td><td class="calcbutton">5</td><td class="calcbutton">6</td></tr>    
<tr><td class="calcbutton">1</td><td class="calcbutton">2</td><td class="calcbutton">3</td></tr>
<tr><td class="calcbutton">0</td><td class="calcpoint">.</td><td class="calcenter">ENTER</td></tr>    
</table>
</div>

Why does pressing . clear the input?

Upvotes: 13

Views: 1561

Answers (3)

Felix
Felix

Reputation: 2691

If a value is set (per JS), it works like the following (copy from specification)

  1. Let oldValue be the element's value.
  2. Set the element's value to the new value.
  3. Set the element's dirty value flag to true.
  4. Invoke the value sanitization algorithm, if the element's type attribute's current state defines one.
  5. If the element's value (after applying the value sanitization algorithm) is different from oldValue, and the element has a text entry cursor position, move the text entry cursor position to the end of the text control, unselecting any selected text and resetting the selection direction to "none".

That means it is immediately checked whether it fits the pattern of a number, whereas on a user input, it waits to a later point (basically to allow the ability to add decimal points)

See https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value for more information.

Upvotes: 11

hendra saputra
hendra saputra

Reputation: 1

document.getElementById('2').onclick = () => document.getElementById('input').value += '2';
document.getElementById('.').onclick = () => document.getElementById('input').value += '.';
<input id="input" type="number" value="3"></input>
<div id="2">click to add 2</div>
<div id=".">click to add .</div>

Upvotes: -2

KooiInc
KooiInc

Reputation: 122906

I suppose it's because of the number type of the input field and its sanitation (by the interpreter, see @Felix' answer).

If you want to keep using the number field, an idea to work around it may be to use a hidden text field to fill the number field (using parseFloat). Here's a snippet (text field not hidden for demo), using event delegation for the handling.

document.addEventListener(`click`, handle);

function handle(evt) {
  if (evt.target.matches(`.calcbutton`)) {
    const value = evt.target.textContent;
    const inpNr = document.querySelector('#input');
    const inpTxt = document.querySelector('#inputTxt');
    const hasDot = /\./.test(inpTxt.value);
    // only one dot allowed
    inpTxt.value += hasDot && value === `.` ? `` : value;
    return inpNr.value = parseFloat(inpTxt.value);
  }
  
  // for demo: hide text or number field to see what it's like
  if (evt.target.matches(`[data-hidden]`)) {
    if (evt.target.dataset.hidden === `txt`) {
      document.querySelector('#input').closest(`div`).style.display = `none`;
      document.querySelector('#inputTxt').closest(`div`).style.display = ``;
      evt.target.dataset.hidden = `nr`;
      return evt.target.textContent = `Hide text field`;
    }
    
    document.querySelector('#input').closest(`div`).style.display = ``;
    document.querySelector('#inputTxt').closest(`div`).style.display = `none`;
    evt.target.dataset.hidden = `txt`;
    return evt.target.textContent = `Hide number field`;
  }
}
.calcbutton {
  cursor: pointer;
  font-size: 1.5rem;
  font-weight: bold;
}
<div><input id="input" type="number" readonly> Number</div>
<div style="display:none"><input id="inputTxt" type="text" readonly> Text</div>
<div id="calc">
  <table>
    <tr>
      <td class="calcbutton">7</td>
      <td class="calcbutton">8</td>
      <td class="calcbutton">9</td>
    </tr>
    <tr>
      <td class="calcbutton">4</td>
      <td class="calcbutton">5</td>
      <td class="calcbutton">6</td>
    </tr>
    <tr>
      <td class="calcbutton">1</td>
      <td class="calcbutton">2</td>
      <td class="calcbutton">3</td>
    </tr>
    <tr>
      <td class="calcbutton">0</td>
      <td class="calcbutton">.</td>
      <td class="calcenter">ENTER</td>
    </tr>
  </table>
</div>
<button data-hidden="txt">Hide number field</button>

Upvotes: 5

Related Questions