テキストボックス 3 — 入力補助・特殊入力

フォーム 中級

このコンポーネントについて

テキスト入力には基本的な文字入力以外に、用途に特化した「特殊入力UI」が存在します。 パスワードの表示切替・検索ボックスのクリアボタン・OTP分割入力は、それぞれログイン・検索・二段階認証という頻出シーンで必ず使われる定番パターンです。

いずれもHTML・CSS・バニラJSのみで実装でき、外部ライブラリは不要です。 このページでは3つの特殊入力UIを動くデモで確認でき、そのままコピペして使えます。

  • パスワード(表示/非表示トグル) — 目のアイコンボタンでパスワードの表示・非表示を切り替え。type="password"type="text" の切り替えで実現
  • 検索ボックス(×クリアボタン付き) — 入力値があるときだけ×ボタンをフェードイン表示。クリックで値をクリアしてフォーカスを戻す
  • OTP入力(6桁・分割ボックス) — 1文字入力で次ボックスへ自動フォーカス移動。Backspaceで前ボックスへ戻る。まとめて貼り付けにも対応

デモ

Pattern 1 — パスワード(表示/非表示トグル)

Pattern 2 — 検索ボックス(×クリアボタン付き)

Pattern 3 — OTP入力(6桁・分割ボックス)

※ 数字のみ入力可。1文字入力で次へ自動移動。まとめてペーストも可

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>入力補助・特殊入力 サンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<!-- ============================================================
     Pattern 1: パスワード 表示/非表示トグル
     ============================================================ -->
<div class="tb3-pattern">
  <label class="tb3-field-label" for="password-input">パスワード</label>
  <div class="tb3-password-wrap">
    <input type="password" id="password-input" class="tb3-input"
           placeholder="パスワードを入力" autocomplete="current-password">
    <button type="button" class="tb3-toggle-btn" id="password-toggle"
            aria-label="パスワードを表示">
      <!-- 目アイコン: 非表示状態のときに表示 -->
      <svg class="icon-eye" xmlns="http://www.w3.org/2000/svg" width="18" height="18"
           viewBox="0 0 24 24" fill="none" stroke="currentColor"
           stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
        <circle cx="12" cy="12" r="3"/>
      </svg>
      <!-- 目に斜線アイコン: 表示状態のときに表示 -->
      <svg class="icon-eye-off" xmlns="http://www.w3.org/2000/svg" width="18" height="18"
           viewBox="0 0 24 24" fill="none" stroke="currentColor"
           stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
           style="display:none">
        <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8
                 a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4
                 c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07
                 a3 3 0 1 1-4.24-4.24"/>
        <line x1="1" y1="1" x2="23" y2="23"/>
      </svg>
    </button>
  </div>
</div>

<!-- ============================================================
     Pattern 2: 検索ボックス ×クリアボタン付き
     ============================================================ -->
<div class="tb3-pattern">
  <label class="tb3-field-label" for="search-input">検索</label>
  <div class="tb3-search-wrap">
    <!-- 虫眼鏡アイコン(常時表示) -->
    <svg class="tb3-search-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16"
         viewBox="0 0 24 24" fill="none" stroke="currentColor"
         stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <circle cx="11" cy="11" r="8"/>
      <line x1="21" y1="21" x2="16.65" y2="16.65"/>
    </svg>
    <input type="search" id="search-input" class="tb3-input tb3-input--search"
           placeholder="キーワードを入力" autocomplete="off">
    <!-- ×ボタン: 入力があるときだけ表示(CSS の .visible クラスで制御) -->
    <button type="button" class="tb3-clear-btn" id="search-clear"
            aria-label="入力をクリア">
      <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
           viewBox="0 0 24 24" fill="none" stroke="currentColor"
           stroke-width="2.5" stroke-linecap="round">
        <line x1="18" y1="6" x2="6" y2="18"/>
        <line x1="6" y1="6" x2="18" y2="18"/>
      </svg>
    </button>
  </div>
</div>

<!-- ============================================================
     Pattern 3: OTP 6桁 分割入力
     ============================================================ -->
<div class="tb3-pattern">
  <label class="tb3-field-label">認証コード(6桁)</label>
  <div class="tb3-otp-wrap">
    <!-- autocomplete="one-time-code" で SMS 認証コードの自動入力を促す(先頭のみ設定) -->
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]" autocomplete="one-time-code">
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]">
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]">
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]">
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]">
    <input class="tb3-otp-input" type="text" inputmode="numeric"
           maxlength="1" pattern="[0-9]">
  </div>
  <span class="tb3-hint">※ 数字のみ入力可。1文字入力で次へ自動移動。まとめてペーストも可</span>
</div>

<script src="./script.js"></script>
</body>
</html>
/* === 入力補助・特殊入力 サンプル ===
   色を変えたいときは :root の変数を書き換えるだけでOKです */
:root {
  --color-accent:  #2B7FE8; /* フォーカス時のボーダー・アクセント色 */
  --color-text:    #1A2332; /* 入力テキスト色 */
  --color-label:   #5A6A7A; /* ラベル文字色 */
  --color-border:  #D0D7E0; /* 通常時のボーダー色 */
  --color-bg:      #F4F6F9; /* カード背景色 */
  --color-icon:    #9AA5B4; /* アイコン色 */
  --color-otp-filled: #EEF4FD; /* OTP 入力済みボックスの背景色 */
}

/* 各パターンを囲むカード */
.tb3-pattern {
  margin-bottom: 16px;
  padding: 20px;
  background: var(--color-bg);
  border-radius: 8px;
  max-width: 520px;
}
.tb3-pattern:last-child { margin-bottom: 0; }

/* フィールドラベル */
.tb3-field-label {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
  font-weight: 500;
  color: var(--color-label);
}

/* input 共通スタイル */
.tb3-input {
  width: 100%;
  padding: 9px 12px;
  border: 1.5px solid var(--color-border);
  border-radius: 6px;
  font-size: 14px;
  color: var(--color-text);
  background: #fff;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s;
  box-sizing: border-box;
  font-family: sans-serif;
}
.tb3-input:focus {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(43, 127, 232, 0.12);
}


/* ============================================================
   Pattern 1: パスワード 表示/非表示トグル
   ============================================================ */

/* トグルボタンを右端に重ねるために relative を設定 */
.tb3-password-wrap { position: relative; }

/* ボタン幅分のパディングを右側に確保する */
.tb3-password-wrap .tb3-input { padding-right: 44px; }

/* 目のアイコンボタン: 入力欄の右端に絶対配置 */
.tb3-toggle-btn {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  width: 40px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-icon);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s;
}
.tb3-toggle-btn:hover { color: var(--color-label); }


/* ============================================================
   Pattern 2: 検索ボックス × クリアボタン
   ============================================================ */

/* 虫眼鏡・×ボタンを input に重ねるために relative を設定 */
.tb3-search-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

/* 虫眼鏡アイコン: 左端に絶対配置。pointer-events:none でクリックを透過させる */
.tb3-search-icon {
  position: absolute;
  left: 12px;
  color: var(--color-icon);
  pointer-events: none;
}

/* 左右アイコン分のパディングを確保 */
.tb3-input--search {
  padding-left: 36px;
  padding-right: 36px;
}

/* ブラウザ標準の×ボタンを非表示にする(Chrome/Safari が追加する) */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

/* ×ボタン: 右端に絶対配置。opacity で表示/非表示をアニメーション */
.tb3-clear-btn {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  width: 36px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-icon);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none; /* 非表示中はクリック不可にする */
  transition: opacity 0.15s, color 0.15s;
}
/* .visible を付けると表示・クリック可能になる */
.tb3-clear-btn.visible {
  opacity: 1;
  pointer-events: auto;
}
.tb3-clear-btn:hover { color: var(--color-label); }


/* ============================================================
   Pattern 3: OTP 6桁 分割入力
   ============================================================ */

/* ボックスを横一列に並べる */
.tb3-otp-wrap {
  display: flex;
  gap: 8px;
}

/* 1文字用の正方形ボックス */
.tb3-otp-input {
  width: 44px;
  height: 52px;
  padding: 0;
  text-align: center;
  font-size: 22px;
  font-weight: 600;
  color: var(--color-text);
  background: #fff;
  border: 1.5px solid var(--color-border);
  border-radius: 6px;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s;
  font-family: sans-serif;
  box-sizing: border-box;
  caret-color: transparent; /* テキストカーソルを非表示にしてボックス感を出す */
}
.tb3-otp-input:focus {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(43, 127, 232, 0.12);
}
/* 入力済みのボックスに薄い背景色を付ける */
.tb3-otp-input.filled { background: var(--color-otp-filled); }

/* ヒントテキスト */
.tb3-hint {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  color: var(--color-icon);
}

/* スマホ対応 */
@media (max-width: 600px) {
  .tb3-pattern { max-width: 100%; }
  .tb3-otp-input { width: 38px; height: 46px; font-size: 18px; }
  .tb3-otp-wrap { gap: 6px; }
}
// ================================================================
// Pattern 1: パスワード 表示/非表示 トグル
// ================================================================
var passwordInput  = document.getElementById('password-input');
var passwordToggle = document.getElementById('password-toggle');
// SVGアイコンを2枚用意し、状態に応じて切り替える
var iconEye    = passwordToggle.querySelector('.icon-eye');
var iconEyeOff = passwordToggle.querySelector('.icon-eye-off');

passwordToggle.addEventListener('click', function () {
  var isHidden = passwordInput.type === 'password';

  // type 切り替え: password(マスク)↔ text(平文表示)
  passwordInput.type = isHidden ? 'text' : 'password';

  // アイコン切り替え
  iconEye.style.display    = isHidden ? 'none' : '';
  iconEyeOff.style.display = isHidden ? ''     : 'none';

  // スクリーンリーダー向け: ボタンの役割をラベルで伝える
  passwordToggle.setAttribute(
    'aria-label',
    isHidden ? 'パスワードを非表示' : 'パスワードを表示'
  );
});


// ================================================================
// Pattern 2: 検索ボックス × クリアボタン
// ================================================================
var searchInput = document.getElementById('search-input');
var searchClear = document.getElementById('search-clear');

// 入力のたびに×ボタンの表示状態を更新する
searchInput.addEventListener('input', function () {
  updateClearBtn();
});

// ×ボタンの表示/非表示を切り替える関数
// display ではなく opacity + pointer-events で制御することで
// CSS の transition によるフェードアニメーションが効く
function updateClearBtn() {
  if (searchInput.value.length > 0) {
    searchClear.classList.add('visible');
  } else {
    searchClear.classList.remove('visible');
  }
}

// ×ボタンクリック: 値をクリアしてフォーカスを戻す
searchClear.addEventListener('click', function () {
  searchInput.value = '';
  updateClearBtn();
  searchInput.focus();
});


// ================================================================
// Pattern 3: OTP 6桁 分割入力
// ================================================================
var otpInputs = Array.from(document.querySelectorAll('.tb3-otp-input'));

otpInputs.forEach(function (input, index) {

  // --- キーダウン: 入力フィルタリングと Backspace 処理 ---
  input.addEventListener('keydown', function (e) {

    // Backspace の特別処理
    if (e.key === 'Backspace') {
      if (input.value === '' && index > 0) {
        // 空のボックスで Backspace → 前のボックスへ戻って削除
        e.preventDefault();
        otpInputs[index - 1].value = '';
        otpInputs[index - 1].classList.remove('filled');
        otpInputs[index - 1].focus();
      }
      // 現在のボックスに値があれば通常の削除(ブラウザのデフォルト動作)
      return;
    }

    // Tab・矢印キーはそのまま通過させる
    var passThrough = ['Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
    if (passThrough.indexOf(e.key) !== -1) return;

    // Ctrl+V(ペースト)・Ctrl+A などはそのまま通過させる
    if (e.ctrlKey || e.metaKey) return;

    // 数字以外のキーを無効化
    if (!/^\d$/.test(e.key)) {
      e.preventDefault();
      return;
    }

    // 数字キーが押されたとき、すでに値があれば上書きできるよう一旦クリア
    // (クリアしないとブラウザによっては maxlength=1 のため入力を弾く)
    if (input.value.length >= 1) {
      input.value = '';
      input.classList.remove('filled');
    }
  });

  // --- インプット: 1文字入力されたら次のボックスへ自動フォーカス ---
  input.addEventListener('input', function () {
    if (input.value.length === 1) {
      input.classList.add('filled');
      // 最後のボックスでなければ次へ移動
      if (index < otpInputs.length - 1) {
        otpInputs[index + 1].focus();
      }
    } else {
      input.classList.remove('filled');
    }
  });

  // --- ペースト: 複数桁をまとめて各ボックスに分配 ---
  input.addEventListener('paste', function (e) {
    e.preventDefault();

    // クリップボードのテキストから数字のみ抽出
    var digits = e.clipboardData.getData('text').replace(/\D/g, '');

    // ペーストした位置のボックスから順に 1 文字ずつセット
    for (var i = 0; i < digits.length; i++) {
      var target = otpInputs[index + i];
      if (!target) break; // ボックス数を超えたら終了
      target.value = digits[i];
      target.classList.add('filled');
    }

    // 最後に埋めたボックスの次へフォーカス(末尾なら末尾ボックスにとどまる)
    var nextIndex = Math.min(index + digits.length, otpInputs.length - 1);
    otpInputs[nextIndex].focus();
  });
});

AI用プロンプト

各パターンのプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

Pattern 1 — パスワード(表示/非表示トグル)

# テキストボックス(パスワード表示/非表示トグル)作成依頼

## 概要
パスワード入力フィールドに「表示/非表示」トグルボタンを実装してください。目のアイコンをクリックするたびに表示と非表示が切り替わります。

## 要件
- type="password" のinputフィールドを使用する
- 入力欄の右端に目のアイコンボタンを配置する
- ボタンをクリックするたびに type="password" ↔ type="text" を切り替える
- 非表示状態: 目のアイコン表示、aria-label="パスワードを表示"
- 表示状態: 目に斜線アイコン表示、aria-label="パスワードを非表示"
- フォーカス時にボーダーをアクセントカラー(青)に変化させる

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし(アイコンはSVGで実装)
- レスポンシブ対応:必要

## 動作詳細
入力欄とトグルボタンを親要素(position: relative)で囲み、ボタンを右端に絶対配置する。
クリックイベントで input.type を切り替え、同時にアイコンのSVGを切り替える。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。

Pattern 2 — 検索ボックス(×クリアボタン付き)

# テキストボックス(検索ボックス・×クリアボタン付き)作成依頼

## 概要
左端に虫眼鏡アイコン・右端に×クリアボタンを持つ検索ボックスを実装してください。入力値がある場合だけ×ボタンを表示し、クリックで入力をクリアします。

## 要件
- 左端に虫眼鏡アイコンを常時表示する(SVGまたはUnicode)
- 右端の×ボタンは入力値が空のとき非表示、1文字以上で表示する
- 表示/非表示の切り替えはフェードアニメーション(opacity + transition)で行う
- ×ボタンをクリックすると入力値をクリアし、×ボタンを非表示にして入力欄にフォーカスを戻す
- type="search" のデフォルトUIは CSS で非表示にする
- フォーカス時にボーダーをアクセントカラー(青)に変化させる

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし(アイコンはSVGで実装)
- レスポンシブ対応:必要

## 動作詳細
親要素を position: relative にし、虫眼鏡と×ボタンを absolute で左右に配置する。
inputの左右パディングをアイコン分大きくとる。
input イベントで value.length を見て×ボタンの表示を制御する。
display ではなく opacity + pointer-events で制御するとフェードアニメーションが効く。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。

Pattern 3 — OTP入力(6桁・分割ボックス)

# テキストボックス(OTP入力・6桁分割ボックス)作成依頼

## 概要
二段階認証や認証コード入力に使う、6桁の分割入力フィールドを実装してください。1文字入力で次のボックスに自動移動し、Backspaceで前へ戻ります。

## 要件
- 数字1文字用のinputを6つ横並びに配置する
- 数字(0〜9)以外のキー入力は無効化する
- 1文字入力すると次のボックスへ自動でフォーカスを移動する
- Backspaceキーの処理:
  - 現在のボックスに文字がある場合: 文字を削除(フォーカス移動なし)
  - 現在のボックスが空の場合: 前のボックスへフォーカス移動して文字を削除
- 数字6桁をまとめてペーストすると各ボックスに1文字ずつ自動分配する
- 先頭ボックスに autocomplete="one-time-code" を設定する
- 入力済みのボックスは薄い青背景にして視覚的にわかりやすくする

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
inputを配列(querySelectorAll)で取得し、インデックスで前後のボックスを参照する。
paste イベントで clipboardData.getData('text') から数字のみ抽出し、各ボックスに設定する。
ボックスのサイズは 44px × 52px 程度の正方形にし、文字は大きく(22px)中央寄せにする。
caret-color: transparent でカーソルを非表示にするとボックス感が増す。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。

同じカテゴリーの事例

現在準備中です。