郵便番号自動入力(Zip Code Lookup)— 7桁入力で住所を自動補完

フォーム入力 初級

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

会員登録や配送先入力など住所を伴うフォームでは、郵便番号から都道府県・市区町村・町域を自動入力できると、ユーザーの入力負担を大きく減らせます。 日本の業務フォームでは定番の機能で、検索ニーズも安定して高いパターンです。

このページでは、郵便番号を7桁入力した時点で自動的に検索が走り、住所欄へ自動入力される実装をデモで確認できます。 住所検索には無料・APIキー不要で CORS にも対応した zipcloud 郵便番号検索APIを fetch で直接呼び出すため、ビルド不要のバニラJSだけで完結します。

  • 7桁入力で自動検索 — ボタン操作不要。郵便番号欄から数字のみを抽出し、7桁揃った時点で自動的にAPIへ問い合わせる
  • 住所自動入力 — 取得した都道府県・市区町村・町域を各入力欄へ自動セット
  • 手動編集可能 — 自動入力された住所欄も含めて常に手動で編集できる(番地・建物名はAPIで取得できないため別途手動入力欄を用意)
  • 状態に応じたステータス表示 — 検索中/成功/該当なし/通信エラーの4状態をメッセージで案内し、エラー時も手動入力を促す

実装のポイント・注意点

検索ボタンを押させる方式より、「桁数が揃ったら自動的に検索する」方式のほうが実務では主流で体験も良くなります。 実装も難しくなく、input イベントで value.replace(/\D/g, '') により数字以外を取り除き、文字数が7桁になった瞬間に fetch を実行するだけで成立します。 debounce のような複雑な制御は不要です。

zipcloud APIはAPIキー不要・CORS 対応の無料JSON APIのため、サーバーを介さずブラウザから直接呼び出せます。 ビルド不要・CDN前提の構成とも相性がよく、バニラJSだけで完結する代表的な「外部API連携」の事例です。

注意したいのは、zipcloud APIが返すのは都道府県・市区町村・町域までで、番地や建物名までは取得できない点です。 住所欄に番地まで含めてしまうと自動入力で上書きされてしまうため、番地・建物名は必ず専用の入力欄に分けて手動入力を促してください。 また、自動入力された住所も実際の表記と完全には一致しないことがあるため、入力後も自由に編集できる状態を保つことが大切です。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

※ 数字のみ入力可。7桁揃うと自動的に住所を検索します
※ 番地・建物名は自動入力されません。手動で入力してください

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.jsfetch で外部APIを呼び出すため file:// での直接表示でも動作します(zipcloud APIはCORS対応のため)。 保存時の文字コードは 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>

<div class="zip-form">

  <div class="zip-field">
    <label class="zip-label" for="zip-postal">郵便番号</label>
    <input type="text" id="zip-postal" class="zip-input zip-input-postal"
           inputmode="numeric" maxlength="8" placeholder="例:123-4567" autocomplete="postal-code">
    <p class="zip-status" id="zip-status" aria-live="polite"></p>
    <span class="zip-hint">※ 数字のみ入力可。7桁揃うと自動的に住所を検索します</span>
  </div>

  <div class="zip-field">
    <label class="zip-label" for="zip-pref">都道府県</label>
    <input type="text" id="zip-pref" class="zip-input" autocomplete="address-level1">
  </div>

  <div class="zip-field">
    <label class="zip-label" for="zip-city">市区町村</label>
    <input type="text" id="zip-city" class="zip-input" autocomplete="address-level2">
  </div>

  <div class="zip-field">
    <label class="zip-label" for="zip-town">町域</label>
    <input type="text" id="zip-town" class="zip-input" autocomplete="address-level3">
  </div>

  <div class="zip-field">
    <label class="zip-label" for="zip-detail">番地・建物名</label>
    <input type="text" id="zip-detail" class="zip-input" placeholder="例:1-2-3 ○○ビル101">
    <span class="zip-hint">※ 番地・建物名は自動入力されません。手動で入力してください</span>
  </div>

</div>

<script src="./script.js"></script>
</body>
</html>
:root {
  --color-accent:    #2B7FE8;
  --color-success:   #16A34A;
  --color-notfound:  #B45309;
  --color-error:     #DC2626;
  --color-border:    #D0D7E0;
  --color-text:      #2C3A4A;
  --color-hint:      #8A97A6;
}

body {
  font-family: sans-serif;
  background: #F4F6F9;
  padding: 24px 16px;
  margin: 0;
}

/* フォーム全体 */
.zip-form {
  max-width: 420px;
  margin: 0 auto;
  padding: 24px;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  box-sizing: border-box;
}

/* 各入力欄のブロック */
.zip-field { margin-bottom: 20px; }
.zip-field:last-child { margin-bottom: 0; }

.zip-label {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text);
}

.zip-input {
  width: 100%;
  padding: 10px 12px;
  border: 1.5px solid var(--color-border);
  border-radius: 6px;
  font-size: 15px;
  outline: none;
  transition: border-color 0.15s;
  box-sizing: border-box;
  font-family: sans-serif;
  background: #fff;
}

.zip-input:focus { border-color: var(--color-accent); }

/* 郵便番号欄は他の住所欄より幅を狭くして区別する */
.zip-input-postal { max-width: 200px; }

/* ステータスメッセージ(検索中/成功/該当なし/エラー) */
.zip-status {
  margin: 6px 0 0;
  font-size: 13px;
  min-height: 18px; /* レイアウトシフト防止 */
  line-height: 1.4;
}

.zip-status.searching { color: var(--color-text); }
.zip-status.success   { color: var(--color-success); }
.zip-status.notfound  { color: var(--color-notfound); }
.zip-status.error     { color: var(--color-error); }

/* ヒントテキスト */
.zip-hint {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--color-hint);
}
// zipcloud 郵便番号検索API(APIキー不要・CORS対応)
var ZIPCLOUD_ENDPOINT = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=';

var postalInput = document.getElementById('zip-postal');
var prefInput   = document.getElementById('zip-pref');
var cityInput   = document.getElementById('zip-city');
var townInput   = document.getElementById('zip-town');
var detailInput = document.getElementById('zip-detail');
var statusEl    = document.getElementById('zip-status');

// ステータスメッセージとスタイルをまとめて切り替える
function setStatus(type, message) {
  statusEl.textContent = message;
  statusEl.className = 'zip-status' + (type ? ' ' + type : '');
}

function clearStatus() {
  statusEl.textContent = '';
  statusEl.className = 'zip-status';
}

// 郵便番号から住所を検索し、結果を入力欄へ反映する
function searchAddress(digits) {
  setStatus('searching', '検索中…');

  fetch(ZIPCLOUD_ENDPOINT + digits)
    .then(function (res) {
      return res.json();
    })
    .then(function (data) {
      if (data.results && data.results.length > 0) {
        var address = data.results[0];
        prefInput.value = address.address1; // 都道府県
        cityInput.value = address.address2; // 市区町村
        townInput.value = address.address3; // 町域
        setStatus('success', '✓ 住所を自動入力しました');
      } else {
        setStatus('notfound', '該当する住所が見つかりませんでした。お手数ですが手動で入力してください');
      }
    })
    .catch(function () {
      setStatus('error', '住所の取得に失敗しました。お手数ですが手動で入力してください');
    });
}

// 郵便番号欄の入力を監視し、数字のみ抽出して7桁揃ったら自動検索する
postalInput.addEventListener('input', function () {
  var digits = postalInput.value.replace(/\D/g, '');
  postalInput.value = digits;

  if (digits.length === 7) {
    searchAddress(digits);
  } else {
    // 7桁に満たない・桁数が変わった場合はステータス表示をクリアする
    clearStatus();
  }
});

AI用プロンプト

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

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。ライブラリ指定や項目変更など、要件を追記して使うのがおすすめです。

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

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

# 郵便番号→住所自動入力 作成依頼

## 概要
郵便番号を入力すると、都道府県・市区町村・町域を自動入力するフォームを実装してください。

## 要件
- 入力欄:郵便番号、都道府県、市区町村、町域、番地・建物名
- 郵便番号欄は数字以外の文字を除去し、数字が7桁揃った時点で自動的に住所検索を行う(検索ボタンは不要)
- 住所検索には zipcloud 郵便番号検索API(https://zipcloud.ibsnet.co.jp/api/search?zipcode=XXXXXXX、APIキー不要・CORS対応)を fetch で呼び出す
- 検索中は「検索中…」、成功時は「✓ 住所を自動入力しました」、該当なしの場合は「該当する住所が見つかりませんでした。手動で入力してください」、通信エラー時は「住所の取得に失敗しました。手動で入力してください」とステータスを表示する
- 自動入力された都道府県・市区町村・町域の欄は、その後も手動で編集できるようにする
- 番地・建物名はAPIで取得できないため、専用の入力欄を用意し手動入力を促すヒントを添える
- 郵便番号が7桁未満に変わったらステータス表示をクリアする

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし(外部API: zipcloud 郵便番号検索API)
- レスポンシブ対応:必要

## 動作詳細
郵便番号欄の input イベントで value.replace(/\D/g, '') により数字のみを抽出し、input.value に書き戻す。
抽出した数字が7桁になった時点で fetch を実行し、レスポンスJSONの results を確認する。
results が null の場合は「該当なし」、配列の場合は results[0] の address1(都道府県)/ address2(市区町村)/ address3(町域)を各入力欄にセットする。
通信エラーは try/catch で捕捉してエラーメッセージを表示する。
ステータスメッセージは textContent で設定し、innerHTML は使用しない。

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