Get an input value in JavaScript, for every kind of input

One property, .value, reads a text box. Numbers, dates, checkboxes, radio groups and whole forms each have a better way, and each has a trap.

To get what a user typed into an input, find the element and read its value property:

const name = document.getElementById('name').value;

That is the whole answer for a text box. Two details trip people up: value is always a string, and it only holds what the user typed if you read it after they typed. Try both below.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Read an input value</title>
<style>
  body { margin: 0; padding: 18px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .box { max-width: 460px; background: #fff; border-radius: 12px; padding: 16px 18px; box-shadow: 0 4px 16px rgba(0, 0, 0, .08); }
  label { display: block; font-weight: 600; margin-bottom: 6px; }
  .row { display: flex; gap: 8px; }
  input { flex: 1; min-width: 0; font: inherit; padding: 8px 10px; border: 1px solid #c9ced6; border-radius: 8px; }
  button { font: inherit; padding: 8px 14px; border: 0; border-radius: 8px; background: #2563eb; color: #fff; cursor: pointer; }
  p { margin: 12px 0 0; font-size: 15px; }
  output { font-family: ui-monospace, Consolas, monospace; background: #eef1f5; border-radius: 4px; padding: 1px 5px; word-break: break-all; }
  .count { color: #4b5563; font-size: 14px; }
</style>
</head>
<body>
<div class="box">
  <label for="name">Your name</label>
  <div class="row">
    <input id="name" type="text" placeholder="Type here" autocomplete="off">
    <button id="read" type="button">Read value</button>
  </div>
  <p>Button read: <output id="out">(press the button)</output></p>
  <p>Live, on every keystroke: <output id="live"></output></p>
  <p class="count">input fired <b id="ni">0</b> times, change fired <b id="nc">0</b> times</p>
</div>

<script>
  const box = document.getElementById('name');
  let inputs = 0, changes = 0;

  // Read the value when something happens, not when the page loads
  document.getElementById('read').addEventListener('click', () => {
    document.getElementById('out').textContent = JSON.stringify(box.value); // always a string
  });

  // input: after every edit (each key, paste, delete)
  box.addEventListener('input', () => {
    document.getElementById('live').textContent = box.value;
    document.getElementById('ni').textContent = ++inputs;
  });

  // change: once, when the edit is committed (the box loses focus)
  box.addEventListener('change', () => {
    document.getElementById('nc').textContent = ++changes;
  });
</script>
</body>
</html>
Type a name. The button reads the value on click; the live line reads it on every input event.

Read the value inside an event, not at page load

A script at the bottom of the page runs once, as soon as the page loads. At that moment the box is empty, so a value read there is "", and it never updates by itself.

Put the read inside a listener. A button click, the input event, the change event and a form's submit event all work. addEventListener covers how listeners are attached.

If the script runs before the input exists in the page, getElementById returns null and reading .value throws. In Chrome the error reads:

Cannot read properties of null (reading 'value')

Move the <script> below the input, or add defer to it.

.value is always a string

Every input returns a string from value, even type="number" and type="range". Add two of them with + and JavaScript joins the text instead of adding.

Two number boxes holding 2 and 3: .value joins them into "23", valueAsNumber adds them to 5.
Two number boxes holding 2 and 3: .value joins them into "23", valueAsNumber adds them to 5.

A number input has a property that skips the conversion. valueAsNumber returns a real number. When the box is empty, it returns NaN, which you can test with Number.isNaN().

Box holds value Number(value) valueAsNumber
3 "3" 3 3
2.5 "2.5" 2.5 2.5
nothing "" 0 NaN
1e (not a number yet) "" 0 NaN

The last two rows are the trap. Number("") is 0, so an empty box silently counts as zero. On a number input, text that is not a valid number also gives an empty value, not the text itself.

On a text input, valueAsNumber is always NaN. Convert with Number() or parseFloat() there, and check for an empty string first.

Dates: value or valueAsDate

A date input's value is a string in the form "2026-09-26", whatever format the browser shows on screen. That string sorts correctly and is often all you need.

valueAsDate returns a Date object set to midnight UTC on that day. In a time zone west of UTC, showing it with local methods gives the day before. Measured in a browser set to New York:

day.valueAsDate.toISOString();          // "2026-09-26T00:00:00.000Z"
day.valueAsDate.toLocaleDateString();   // "9/25/2026"

Use the UTC methods, such as getUTCDate(), or pass timeZone: 'UTC' when formatting. valueAsNumber on a date input gives the same moment in milliseconds. JavaScript date format covers formatting.

Checkboxes, selects and radio groups

These controls do not store what the user picked in the place you might expect. Change each one below and watch which property moves.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Every control reads differently</title>
<style>
  body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px; }
  form, .res { background: #fff; border-radius: 12px; padding: 14px 16px; box-shadow: 0 4px 16px rgba(0, 0, 0, .08); }
  .f { margin-bottom: 10px; }
  .f > label, legend { display: block; font-weight: 600; font-size: 14px; margin-bottom: 4px; }
  fieldset { border: 0; padding: 0; margin: 0; }
  fieldset label { margin-right: 12px; font-size: 15px; }
  input[type=number], input[type=date], select { font: inherit; padding: 6px 8px; border: 1px solid #c9ced6; border-radius: 8px; width: 100%; box-sizing: border-box; }
  .res { font-size: 13px; }
  .res div { padding: 5px 0; border-bottom: 1px solid #eef0f3; }
  code { font-family: ui-monospace, Consolas, monospace; color: #1d4ed8; }
  .v { display: block; font-family: ui-monospace, Consolas, monospace; margin-top: 2px; word-break: break-all; }
  .t { color: #6b7280; font-family: system-ui, sans-serif; }
</style>
</head>
<body>
<div class="wrap">
  <form id="f">
    <div class="f"><label for="qty">Quantity (number)</label><input id="qty" name="qty" type="number" value="3"></div>
    <div class="f"><label for="day">Day (date)</label><input id="day" name="day" type="date" value="2026-09-26"></div>
    <div class="f"><label><input id="agree" name="agree" type="checkbox"> I agree (checkbox)</label></div>
    <div class="f"><label for="plan">Plan (select)</label>
      <select id="plan" name="plan">
        <option value="free">Free plan</option>
        <option value="pro" selected>Pro plan</option>
        <option value="team">Team plan</option>
      </select></div>
    <fieldset><legend>Size (radio group)</legend>
      <label><input type="radio" name="size" value="S"> S</label>
      <label><input type="radio" name="size" value="M" checked> M</label>
      <label><input type="radio" name="size" value="L"> L</label>
    </fieldset>
  </form>
  <div class="res" id="res"></div>
</div>

<script>
  const form = document.getElementById('f');
  const qty = document.getElementById('qty');
  const day = document.getElementById('day');
  const agree = document.getElementById('agree');
  const plan = document.getElementById('plan');

  // Show a value with its type, so "3" and 3 look different
  function show(v) {
    if (v instanceof Date) return v.toISOString() + ' <span class="t">Date</span>';
    if (typeof v === 'string') return JSON.stringify(v) + ' <span class="t">string</span>';
    return String(v) + ' <span class="t">' + (v === null ? 'null' : typeof v) + '</span>';
  }

  function render() {
    const rows = [
      ['qty.value', qty.value],
      ['qty.valueAsNumber', qty.valueAsNumber],
      ['day.value', day.value],
      ['day.valueAsDate', day.valueAsDate],
      ['agree.checked', agree.checked],
      ['agree.value', agree.value],
      ['plan.value', plan.value],
      ['plan.selectedOptions[0].text', plan.selectedOptions[0].text],
      ['form.elements.size.value', form.elements.size.value],
    ];
    document.getElementById('res').innerHTML = rows
      .map(([code, v]) => '<div><code>' + code + '</code><span class="v">' + show(v) + '</span></div>')
      .join('');
  }

  form.addEventListener('input', render); // one listener for every control in the form
  render();
</script>
</body>
</html>
Each control, the property to read, and the type of what comes back.
The property to read for each kind of control.
The property to read for each kind of control.
  • Checkbox: read checked, which is true or false. Its value is "on" unless you set a value attribute, and it stays the same whether the box is ticked or not. More in the checkbox guide.
  • Select: value is the value of the chosen option. If the option has no value attribute, you get its text. For the visible label, read selectedOptions[0].text.
  • Radio group: each radio is its own element, so no single element holds the answer. Ask the group instead:
form.elements.size.value;   // "M", or "" when none is checked

Outside a form, find the checked one with a selector:

document.querySelector('input[name="size"]:checked')?.value;

The ?. stops an error when no radio is checked. Radio buttons in HTML covers groups in more depth.

input vs change: when to read

Both events tell you the value has changed. They differ in how often.

input fires after every edit; change fires once when the text box loses focus.
input fires after every edit; change fires once when the text box loses focus.
input change
Text and number boxes After every keystroke, paste or delete Once, when the box loses focus after an edit
Checkbox, radio, select Right after the pick Right after the pick, just after input
Good for Live totals, search as you type, counters Saving, validating a finished entry

Both events bubble, so one listener on the <form> hears every control inside it. Setting value from your own code does not fire either event. If other code listens for it, call your update function directly after setting the value.

The text input guide also covers defaultValue, which keeps the value from the HTML while value follows the user.

Read the whole form with FormData

Reading fields one by one gets long as a form grows. new FormData(form) collects every named field in one step:

const data = new FormData(form);
data.get('plan');      // "pro"
data.has('agree');     // false when the checkbox is unticked
Object.fromEntries(data); // { qty: "3", plan: "pro", size: "M", ... }

It follows the same rules as a real form submission. Fields without a name are skipped, and so are disabled fields and unticked checkboxes. Empty text boxes are included as "". Every value is a string, so numbers still need converting.

FormData in JavaScript covers getAll, files and sending the data with fetch.

A finished example: a live calculator

This order form uses each technique once. valueAsNumber reads the price and quantity, FormData reads the tip, delivery and gift wrap, and one input listener on the form recomputes the total after every change.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Live order calculator</title>
<style>
  body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  form { max-width: 460px; background: #fff; border-radius: 12px; padding: 16px 18px; box-shadow: 0 4px 16px rgba(0, 0, 0, .08); }
  .two { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .f { margin-bottom: 12px; }
  .f > label, legend { display: block; font-weight: 600; font-size: 14px; margin-bottom: 4px; }
  fieldset { border: 0; padding: 0; margin: 0 0 12px; }
  fieldset label { margin-right: 10px; white-space: nowrap; }
  input[type=number], select { font: inherit; padding: 7px 9px; border: 1px solid #c9ced6; border-radius: 8px; width: 100%; box-sizing: border-box; }
  .total { margin-top: 4px; padding: 12px 14px; border-radius: 10px; background: #f4fbf6; border: 1px solid #cfe9d7; }
  .total b { font-size: 24px; }
  .total.err { background: #fff7f5; border-color: #f3d1c8; }
  .total.err b { font-size: 16px; }
  #lines { font-size: 13px; color: #4b5563; margin-top: 4px; }
</style>
</head>
<body>
<form id="order">
  <div class="two">
    <div class="f"><label for="price">Price ($)</label><input id="price" name="price" type="number" min="0" step="0.01" value="12.50"></div>
    <div class="f"><label for="qty">Quantity</label><input id="qty" name="qty" type="number" min="1" step="1" value="2"></div>
  </div>
  <fieldset><legend>Tip</legend>
    <label><input type="radio" name="tip" value="0"> 0%</label>
    <label><input type="radio" name="tip" value="10" checked> 10%</label>
    <label><input type="radio" name="tip" value="15"> 15%</label>
    <label><input type="radio" name="tip" value="20"> 20%</label>
  </fieldset>
  <div class="f"><label for="ship">Delivery</label>
    <select id="ship" name="ship">
      <option value="0">Pick up (free)</option>
      <option value="4.5">Standard ($4.50)</option>
      <option value="9">Express ($9.00)</option>
    </select></div>
  <div class="f"><label><input type="checkbox" name="wrap"> Gift wrap (+$3.00)</label></div>
  <div class="total" id="box"><b id="total"></b><div id="lines"></div></div>
</form>

<script>
  const form = document.getElementById('order');
  const money = (n) => '$' + n.toFixed(2);

  function update() {
    const price = form.elements.price.valueAsNumber; // NaN when empty or invalid
    const qty = form.elements.qty.valueAsNumber;
    const box = document.getElementById('box');

    if (Number.isNaN(price) || Number.isNaN(qty)) {
      box.classList.add('err');
      document.getElementById('total').textContent = 'Enter a price and a quantity';
      document.getElementById('lines').textContent = '';
      return;
    }

    const data = new FormData(form);        // radios, select and checkbox in one go
    const tipPct = Number(data.get('tip')); // "10" -> 10
    const ship = Number(data.get('ship'));
    const wrap = data.has('wrap') ? 3 : 0;  // an unticked checkbox is left out

    const items = price * qty;
    const tip = items * tipPct / 100;
    const total = items + tip + ship + wrap;

    box.classList.remove('err');
    document.getElementById('total').textContent = money(total);
    document.getElementById('lines').textContent =
      'Items ' + money(items) + ' + tip ' + money(tip) + ' + delivery ' + money(ship) + ' + wrap ' + money(wrap);
  }

  form.addEventListener('input', update);                     // every edit, every click
  form.addEventListener('submit', (e) => e.preventDefault()); // Enter does not reload the page
  update();
</script>
</body>
</html>
Change any field and the total updates. Clear the price to see the empty-box check.
  • One listener: an input listener on the form covers the number boxes, the radios, the select and the checkbox.
  • Empty boxes: Number.isNaN() catches a cleared field before it turns the total into NaN.
  • Enter key: pressing Enter in a box would submit the form and reload the page, so the submit listener calls preventDefault().

For a longer walk through building one, see sharing a calculator as a link. For every input type and the value each one gives, see HTML input types.

When it does not work

What you see Cause Fix
The value is always empty Read once at page load Read it inside an event listener
Cannot read properties of null The script runs before the input exists, or the id is wrong Move the script below, add defer, check the id
2 + 3 shows 23 value is a string Use valueAsNumber or Number()
An empty box counts as 0 Number("") is 0 Check for "" or use valueAsNumber and Number.isNaN()
The total shows NaN A number box is empty or invalid Check Number.isNaN() before computing
A checkbox always says "on" Reading value instead of checked Read checked
The radio value is undefined Reading one radio, or none is checked Use the group value, and handle ""
The date is one day off valueAsDate is midnight UTC Use UTC methods, or keep the value string
The update runs only after clicking away Listening to change on a text box Listen to input
A field is missing from FormData No name, or it is disabled or unticked Add a name; use has() for checkboxes

A calculator or a form is easier to try than to describe. A screenshot shows one set of numbers, and an .html attachment may open as plain code on a phone.

To send the working version, paste the page into a NOS document and choose Create share link. HTML to link walks through it.

The page renders as written and its scripts run, so the people you send it to can type their own numbers and watch the total change. If you change the code later, the same link shows the new version.

Questions people ask

How do I get the value of an input in JavaScript?

Find the element and read its value property, for example document.getElementById('name').value. Do it inside an event listener, such as a button click or the input event, so you read what the user has typed by then.

Why does my input value come back empty?

Usually the code runs once when the page loads, before anyone types. Move the read into a click, input or submit listener. On a number input, an empty string also means the box holds something that is not a valid number.

How do I get a number from an input instead of a string?

On an input with type="number", read valueAsNumber. It is NaN when the box is empty. On other inputs, convert with Number(), but check for an empty string first, because Number('') is 0.

Should I use the input event or the change event?

Use input for results that update while the user types, since it fires after every edit. Use change when you only care about the finished value. For text boxes change fires when the box loses focus; for checkboxes, radios and selects it fires right away.

How do I get the selected radio button value?

Read form.elements.<name>.value, where <name> is the name the radios share. It gives the value of the checked radio, or an empty string when none is checked. Without a form, use querySelector with input[name="size"]:checked.

Keep reading