Browse by section

Web Design 日本語

The HTML input Element, Type by Type

The behaviour of <input> changes substantially with the type you pick. This article goes type by type, so you can tell which ones are safe to use as-is and which to avoid in production.

This article was published in 2024 and revised in September 2026. The original code contained an error: a pattern attribute on a <textarea>. That has been corrected, and per-type guidance added.

  • pattern applies to <input> only. It has no effect on <textarea>. Use minlength for a minimum length
  • inputmode, autocomplete, list and pattern are not 2024 additions

Sponsored

Which types to use, and which to avoid

type Verdict Why
text / email / tel / url / password Use freely Behaviour is stable
search Use Gets a clear button
date / time / datetime-local With care UI differs a lot between browsers
number Narrow use only Scroll wheel changes it; leading zeros vanish
range With care Not for precise values
color Use Opens the OS colour picker
file Use accept and capture work well
month / week Avoid Firefox falls back to a plain text field

type=”date”: what to know first

The display format comes from the OS and browser locale, and you cannot change it—not with CSS, not with an attribute.

<label for="birthday">Date of birth</label>
<input type="date" id="birthday" name="birthday"
       min="1900-01-01" max="2024-12-31"
       autocomplete="bday">
  • value is always YYYY-MM-DD, whatever is displayed. That is what gets submitted and what scripts read
  • min and max also use YYYY-MM-DD
  • Empty means an empty string, and valueAsDate returns null

Careful with valueAsDate

const input = document.getElementById('birthday');

input.value;        // "2024-06-09" (string)
input.valueAsDate;  // Date object — but UTC-based

valueAsDate is interpreted as midnight UTC. In a non-UTC timezone the date can come out one day off, so for date-only values, working with the string is safer.

// can be off by a day
const d = input.valueAsDate;
console.log(d.getDate());

// stay with the string
const [y, m, day] = input.value.split('-').map(Number);

Make the calendar easier to open

input.addEventListener('click', () => {
  if (!('showPicker' in HTMLInputElement.prototype)) return;
  try { input.showPicker(); } catch {}
});

Sponsored

type=”number”: narrow it down

Do not reach for it whenever a field holds digits. Postal codes, phone numbers and card numbers are all wrong fits.

  • Leading zeros are dropped (0120 becomes 120)
  • Hyphens cannot be typed
  • The scroll wheel silently changes the value
  • Long numbers can render in exponential notation

It fits quantities you want to increment—and even then, always give it min, max and step.

<!-- quantity: type="number" is right here -->
<label for="qty">Quantity</label>
<input type="number" id="qty" name="qty" value="1" min="1" max="99" step="1">

<!-- postal code: use text -->
<label for="zip">Postal code</label>
<input type="text" id="zip" name="zip"
       inputmode="numeric" autocomplete="postal-code"
       pattern="\d{3}-?\d{4}"
       title="Enter as 123-4567 or 1234567">

Disable the wheel to prevent accidental edits.

document.querySelectorAll('input[type="number"]').forEach((el) => {
  el.addEventListener('wheel', (e) => {
    if (document.activeElement === el) e.preventDefault();
  }, { passive: false });
});

{ passive: false } is required, or preventDefault() is ignored.

type=”file”: accept and capture

<!-- multiple images -->
<input type="file" name="photos" accept="image/*" multiple>

<!-- open the rear camera on mobile -->
<input type="file" name="doc" accept="image/*" capture="environment">

<!-- filter by extension -->
<input type="file" name="sheet" accept=".csv,.xlsx,text/csv">

accept is not validation. Users can switch the dialog to “All Files”. Always check type and size on the server.

const MAX = 5 * 1024 * 1024; // 5MB

fileInput.addEventListener('change', () => {
  for (const file of fileInput.files) {
    if (!file.type.startsWith('image/')) {
      alert('Please choose an image file');
      fileInput.value = '';   // clear the selection
      return;
    }
    if (file.size > MAX) {
      alert('Please choose a file under 5MB');
      fileInput.value = '';
      return;
    }
  }
});

fileInput.value = '' clears the selection. Without it, the rejected filename stays displayed.

Sponsored

type=”color” and type=”range”

<label for="theme">Theme colour</label>
<input type="color" id="theme" name="theme" value="#2a7fff">

<label for="volume">Volume</label>
<input type="range" id="volume" name="volume"
       min="0" max="100" step="5" value="50"
       list="volume-marks">
<datalist id="volume-marks">
  <option value="0"></option>
  <option value="50"></option>
  <option value="100"></option>
</datalist>
<output for="volume" id="volume-out">50</output>

Adding list to a range gives you tick marks. And since a range shows no numeric value, always pair it with <output>.

const volume = document.getElementById('volume');
const out = document.getElementById('volume-out');

volume.addEventListener('input', () => {
  out.textContent = volume.value;
});

Know the difference between input and change: input fires continuously while dragging; change fires once on release. Update the display on input, save to the server on change.

Recolour the controls with accent-color—more robust than rebuilding them with appearance: none.

:root { accent-color: #2a7fff; }

pattern does not work on textarea

This was the error in the original article. pattern belongs to <input> and does not exist on <textarea>.

<!-- has no effect -->
<textarea required pattern=".{10,}" title="At least 10 characters"></textarea>

<!-- correct: use minlength -->
<label for="feedback">Feedback</label>
<textarea id="feedback" name="feedback"
          required minlength="10" maxlength="1000"
          placeholder="Tell us what you think"></textarea>

Two things about minlength: it does not apply until the user has typed something (required fires first), and newlines count as characters.

To grow the field with its content, CSS field-sizing now handles it.

textarea {
  field-sizing: content;
  min-height: 4lh;
  max-height: 20lh;
  width: 100%;
}

A character counter pairs with maxlength.

const ta = document.getElementById('feedback');
const counter = document.getElementById('counter');

ta.addEventListener('input', () => {
  counter.textContent = `${ta.value.length} / ${ta.maxLength}`;
});

A registration form, put together

<form>
  <label for="username">Username</label>
  <input type="text" id="username" name="username"
         required autocomplete="username" minlength="3">

  <label for="email">Email</label>
  <input type="email" id="email" name="email"
         required autocomplete="email">

  <label for="password">Password</label>
  <input type="password" id="password" name="password"
         required autocomplete="new-password" minlength="8">

  <label for="birthdate">Date of birth</label>
  <input type="date" id="birthdate" name="birthdate"
         required autocomplete="bday" max="2024-12-31">

  <label for="phone">Phone</label>
  <input type="tel" id="phone" name="phone"
         autocomplete="tel" inputmode="tel">

  <button type="submit">Register</button>
</form>
  • Sign-up uses autocomplete="new-password". Using current-password here confuses password managers
  • <button type="submit"> rather than <input type="submit">, so you can put icons or markup inside
  • type="tel" has no built-in validation—formats vary by country. Add pattern if you need a specific one

Style errors with :user-invalid, not :invalid, or everything turns red on page load.

input:user-invalid {
  border-color: #d33;
}

More on this in HTML form features worth using today.

datalist accepts values outside the list

<label for="search">Search products</label>
<input type="search" id="search" list="products"
       enterkeyhint="search" placeholder="Product name">
<datalist id="products">
  <option value="Smartphone"></option>
  <option value="Laptop"></option>
  <option value="Tablet"></option>
  <option value="Headphones"></option>
</datalist>

<datalist> is a suggestion, not a constraint. Values outside the list submit fine. Use <select> to enforce the choices.

  • The dropdown cannot be styled with CSS—it is browser UI
  • Whether it matches by prefix or substring is browser-dependent
  • It becomes unwieldy past a few dozen entries

Attribute-level detail is covered in getting the most out of the HTML input element.

Summary

  • pattern is for <input> only. Use minlength on <textarea>
  • type="date" always has a YYYY-MM-DD value; valueAsDate is UTC-based and can shift by a day
  • type="number" is for quantities you increment—not postal codes or phone numbers
  • Block the scroll wheel on number inputs with { passive: false }
  • accept is not validation. Verify on the server. value = '' clears a selection
  • Pair type="range" with <output>; update on input, save on change
  • <datalist> does not restrict input—use <select> for that
  • Sign-up passwords use autocomplete="new-password"

One wrong type can produce a field users simply cannot fill in. Asking “is this a value you increment?” and “is the format the same worldwide?” gets you to the right answer most of the time.