💻

郵便番号を住所に変換するサイトを作成する

2024/05/02に公開

概要

JavaScript で郵便番号を住所に変換するサイトを作成します。

  • 郵便番号の入力チェックに正規表現を使用する
  • 郵便番号と住所の変換で非同期処理を使用する
  • 住所をクリップボードにコピーする

完成画像

完成画像
完成画像

実装

プロジェクトを作成する

1. プロジェクトを作成する

プロジェクトのディレクトリを任意の場所に作成し、Visual Studio Code でプロジェクトを開いてください。

2. HTML ファイルを作成する

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>郵便番号を住所に変換する</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body class="flex-col flex-center">
    <div class="container flex-col flex-center w-320 gap-md">
      <h1 class="text">郵便番号を住所に変換する</h1>
    </div>

    <script type="module" src="main.js" defer></script>
  </body>
</html>

3. CSS ファイルを作成する

style.css
* {
  margin: 0;
  padding: 0;
}

/* objectの上にあるボタンのクリックイベントを発火させる */
object {
  pointer-events: none;
}

body {
  height: 100vh;
}

.container {
  padding: 20px;
  border-radius: 10px;
  border: 1px solid #ccc;
}

#zipcode {
  flex-grow: 1;
  padding: 2px 16px;
}

/* 横幅 */
.w-240 {
  width: 240px;
}
.w-320 {
  width: 320px;
}
.w-full {
  width: 100%;
}

/* フレックスボックス */
.flex-col {
  display: flex;
  flex-direction: column;
}
.flex-row {
  display: flex;
  flex-direction: row;
}
.flex-center {
  justify-content: center;
  align-items: center;
}
.flex-between {
  justify-content: space-between;
  align-items: center;
}
.flex-start {
  justify-content: flex-start;
  align-items: center;
}
/* グリッドレイアウト */
/* .grid {
  display: grid;
  grid-template-columns: 5fr 1fr;
} */

/* フレックスボックやグリッドレイアウトの空白 */
.gap-sm {
  gap: 10px;
}
.gap-md {
  gap: 20px;
}
.gap-lg {
  gap: 30px;
}

/* テキストサイズ */
.text {
  font-size: 20px;
}
.error {
  color: red;
}

/* ボタン */
button {
  background: #50b894;
  color: #fff;
  border: none;
  padding: 10px;
  cursor: pointer;
}
button:hover {
  background: #38a06f;
}

4. JavaScript ファイルを作成する

main.js

5. SVG ファイルを作成する

<!--?xml version="1.0" encoding="utf-8"?-->
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#4B4B4B;}
</style>
<g>
	<rect x="115.774" y="335.487" class="st0" width="194.387" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="115.774" y="260.208" class="st0" width="194.387" height="18.527" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="115.774" y="184.862" class="st0" width="194.387" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="218.582" y="109.576" class="st0" width="91.58" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<path class="st0" d="M385.112,396.188V39.614c0-2.294-0.197-4.603-0.592-6.768C381.3,14.19,365.006,0,345.438,0H184.686
		c-11.561,0-22.598,4.603-30.741,12.747L53.637,112.986c-8.151,8.22-12.747,19.249-12.747,30.818v252.384
		c0,21.802,17.806,39.607,39.683,39.607h264.864C367.308,435.795,385.112,417.99,385.112,396.188z M170.634,27.529v89.074
		c0,9.662-3.745,13.399-13.339,13.399H68.222L170.634,27.529z M63.163,396.188V149.775h106.02c3.486,0,6.768-0.85,9.655-2.362
		c4.079-2.036,7.361-5.324,9.328-9.328c1.519-2.894,2.302-6.115,2.302-9.526V22.272h154.97c7.156,0,13.331,4.33,15.959,10.574
		c0.92,2.104,1.376,4.337,1.376,6.768v356.574c0,9.518-7.748,17.342-17.335,17.342H80.574
		C70.98,413.53,63.163,405.706,63.163,396.188z" style="fill: rgb(75, 75, 75);"></path>
	<path class="st0" d="M431.488,76.205h-26.732l1.375,22.272h25.356c9.594,0,17.349,7.748,17.349,17.342v356.573
		c0,9.519-7.755,17.342-17.349,17.342H166.562c-7.163,0-13.339-4.406-15.968-10.581c-0.85-2.097-1.374-4.33-1.374-6.761V456.89
		h-22.272v15.503c0,2.294,0.198,4.589,0.593,6.761c3.22,18.588,19.515,32.846,39.022,32.846h264.926
		c21.877,0,39.622-17.805,39.622-39.607V115.82C471.11,93.943,453.365,76.205,431.488,76.205z" style="fill: rgb(75, 75, 75);"></path>
</g>
</svg>

郵便番号の入力欄を検証する

1. 郵便番号を入力するフォームを作成する

index.html
<div class="container flex-col flex-center w-320 gap-md">
  <h1 class="text">郵便番号を住所に変換する</h1>

  <form id="form" class="flex-col flex-center gap-sm w-full">
    <div class="flex-row gap-sm w-full">
      <input type="text" id="zipcode" placeholder="郵便番号" class="text" />
      <button type="submit">検索</button>
    </div>
    <div id="zipcodeError" class="error"></div>
  </form>
</div>

2. submit イベントの中で郵便番号を検証する

main.js
/* ****************************************
  要素一覧
**************************************** */

const formElement = document.getElementById("form");
const zipcodeElement = document.getElementById("zipcode");
const zipcodeErrorElement = document.getElementById("zipcodeError");

/* ****************************************
  処理
**************************************** */

formElement.addEventListener("submit", onSubmit);

/* ****************************************
  関数一覧
**************************************** */

/** 郵便番号を検証する */
function validateZipcode(zipcode) {
  // 郵便番号の正規表現
  const pattern = /^\d{3}-?\d{4}$/;
  // 入力された郵便番号が正しいかどうかを返す
  return pattern.test(zipcode);
}

/** 郵便番号のエラーメッセージを設定する */
function setZipcodeError(isValid) {
  // エラーメッセージを設定する
  if (isValid) {
    zipcodeErrorElement.innerText = "";
  } else {
    zipcodeErrorElement.innerText = "郵便番号が正しくありません";
  }
}

/* ****************************************
  イベントの関数一覧
**************************************** */

async function onSubmit(event) {
  // イベントをキャンセルする
  event.preventDefault();

  // 郵便番号を取得する
  const zipcode = zipcodeElement.value;
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  const isValid = validateZipcode(zipcode);
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  setZipcodeError(isValid);

  if (!isValid) {
    return;
  }
  console.log("submit");
}

郵便番号を住所に変換する

1. 住所の表示欄を作成する

index.html
<div class="container flex-col flex-center w-320 gap-md">
  <h1 class="text">郵便番号を住所に変換する</h1>

  <form id="form" class="flex-col flex-center gap-sm w-full">
    <div class="flex-row gap-sm w-full">
      <input type="text" id="zipcode" placeholder="郵便番号" class="text" />
      <button type="submit">検索</button>
    </div>
    <div id="zipcodeError" class="error"></div>
  </form>

  <div class="flex-row flex-between gap-sm w-full">
    <div class="flex-row flex-start gap-sm text">
      <div>住所:</div>
      <div id="address"></div>
    </div>
  </div>
</div>

2. 非同期処理で郵便番号を住所に変換する

main.js
/* ****************************************
  要素一覧
**************************************** */

const addressElement = document.getElementById("address");

/* ****************************************
  イベントの関数一覧
**************************************** */

async function onSubmit(event) {
  // イベントをキャンセルする
  event.preventDefault();

  // 郵便番号を取得する
  const zipcode = zipcodeElement.value;
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  const isValid = validateZipcode(zipcode);
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  setZipcodeError(isValid);

  if (!isValid) {
    return;
  }

  // 郵便番号を住所に変換するWebAPIのURL
  const url = `https://zipcloud.ibsnet.co.jp/api/search?zipcode=${zipcode}`;
  try {
    // WebAPIにリクエストを送信する
    const response = await fetch(url);
    // レスポンスデータをJSON形式からオブジェクトに変換する
    const data = await response.json();
    // 住所を表示する
    const result = data.results[0];
    addressElement.innerText = `${result.address1}${result.address2}${result.address3}`;
  } catch (error) {
    // 郵便番号から住所を取得できなかったメッセージを表示する
    addressElement.innerText = "住所が取得できませんでした";
  }
}

住所をクリップボードにコピーする

1. クリップボードに保存するボタンを作成する

index.html
<div class="container flex-col flex-center w-320 gap-md">
  <h1 class="text">郵便番号を住所に変換する</h1>

  <form id="form" class="flex-col flex-center gap-sm w-full">
    <div class="flex-row gap-sm w-full">
      <input type="text" id="zipcode" placeholder="郵便番号" class="text" />
      <button type="submit">検索</button>
    </div>
    <div id="zipcodeError" class="error"></div>
  </form>

  <div class="flex-row flex-between gap-sm w-full">
    <div class="flex-row flex-start gap-sm text">
      <div>住所:</div>
      <div id="address"></div>
    </div>
    <button type="button" id="copy" class="flex-col flex-center">
      <object data="clipboard.svg" width="24" height="24"></object>
    </button>
  </div>
</div>

2. クリップボードに住所をコピーする

main.js
/* ****************************************
  要素一覧
**************************************** */

const copyElement = document.getElementById("copy");

/* ****************************************
  処理
**************************************** */

copyElement.addEventListener("click", copyToClipboard);

/* ****************************************
  イベントの関数一覧
**************************************** */

/** クリップボードにコピーする */
function copyToClipboard() {
  // 住所を取得する
  const address = addressElement.innerText;

  // 住所が空の場合は何もしない
  if (address.length === 0) {
    return;
  }

  // 住所をクリップボードにコピーする
  try {
    navigator.clipboard.writeText(address);
    alert("住所をコピーしました");
  } catch (error) {
    alert("コピーに失敗しました");
  }
}

完成のソースコード

index.html
index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>郵便番号を住所に変換する</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body class="flex-col flex-center">
    <div class="container flex-col flex-center w-320 gap-md">
      <h1 class="text">郵便番号を住所に変換する</h1>

      <form id="form" class="flex-col flex-center gap-sm w-full">
        <div class="flex-row gap-sm w-full">
          <input type="text" id="zipcode" placeholder="郵便番号" class="text" />
          <button type="submit">検索</button>
        </div>
        <div id="zipcodeError" class="error"></div>
      </form>

      <div class="flex-row flex-between gap-sm w-full">
        <div class="flex-row flex-start gap-sm text">
          <div>住所:</div>
          <div id="address"></div>
        </div>
        <button type="button" id="copy" class="flex-col flex-center">
          <object data="clipboard.svg" width="24" height="24"></object>
        </button>
      </div>
    </div>

    <script type="module" src="main.js" defer></script>
  </body>
</html>
style.css
style.css
* {
  margin: 0;
  padding: 0;
}

/* objectの上にあるボタンのクリックイベントを発火させる */
object {
  pointer-events: none;
}

body {
  height: 100vh;
}

.container {
  padding: 20px;
  border-radius: 10px;
  border: 1px solid #ccc;
}

#zipcode {
  flex-grow: 1;
  padding: 2px 16px;
}

/* 横幅 */
.w-240 {
  width: 240px;
}
.w-320 {
  width: 320px;
}
.w-full {
  width: 100%;
}

/* フレックスボックス */
.flex-col {
  display: flex;
  flex-direction: column;
}
.flex-row {
  display: flex;
  flex-direction: row;
}
.flex-center {
  justify-content: center;
  align-items: center;
}
.flex-between {
  justify-content: space-between;
  align-items: center;
}
.flex-start {
  justify-content: flex-start;
  align-items: center;
}
/* グリッドレイアウト */
/* .grid {
  display: grid;
  grid-template-columns: 5fr 1fr;
} */

/* フレックスボックやグリッドレイアウトの空白 */
.gap-sm {
  gap: 10px;
}
.gap-md {
  gap: 20px;
}
.gap-lg {
  gap: 30px;
}

/* テキストサイズ */
.text {
  font-size: 20px;
}
.error {
  color: red;
}

/* ボタン */
button {
  background: #50b894;
  color: #fff;
  border: none;
  padding: 10px;
  cursor: pointer;
}
button:hover {
  background: #38a06f;
}
main.js
main.js
"use strict";

/* ****************************************
  要素一覧
**************************************** */

const formElement = document.getElementById("form");
const zipcodeElement = document.getElementById("zipcode");
const zipcodeErrorElement = document.getElementById("zipcodeError");
const addressElement = document.getElementById("address");
const copyElement = document.getElementById("copy");

/* ****************************************
  処理
**************************************** */

formElement.addEventListener("submit", onSubmit);
copyElement.addEventListener("click", copyToClipboard);

/* ****************************************
  関数一覧
**************************************** */

/** 郵便番号を検証する */
function validateZipcode(zipcode) {
  // 郵便番号の正規表現
  const pattern = /^\d{3}-?\d{4}$/;
  // 入力された郵便番号が正しいかどうかを返す
  return pattern.test(zipcode);
}

/** 郵便番号のエラーメッセージを設定する */
function setZipcodeError(isValid) {
  // エラーメッセージを設定する
  if (isValid) {
    zipcodeErrorElement.innerText = "";
  } else {
    zipcodeErrorElement.innerText = "郵便番号が正しくありません";
  }
}

/* ****************************************
  イベントの関数一覧
**************************************** */

async function onSubmit(event) {
  // イベントをキャンセルする
  event.preventDefault();

  // 郵便番号を取得する
  const zipcode = zipcodeElement.value;
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  const isValid = validateZipcode(zipcode);
  // 郵便番号が正しくない場合は、エラーメッセージを表示する
  setZipcodeError(isValid);

  if (!isValid) {
    return;
  }

  // 郵便番号を住所に変換するWebAPIのURL
  const url = `https://zipcloud.ibsnet.co.jp/api/search?zipcode=${zipcode}`;
  try {
    // WebAPIにリクエストを送信する
    const response = await fetch(url);
    // レスポンスデータをJSON形式からオブジェクトに変換する
    const data = await response.json();
    // 住所を表示する
    const result = data.results[0];
    addressElement.innerText = `${result.address1}${result.address2}${result.address3}`;
  } catch (error) {
    // 郵便番号から住所を取得できなかったメッセージを表示する
    addressElement.innerText = "住所が取得できませんでした";
  }
}

/** クリップボードにコピーする */
function copyToClipboard() {
  // 住所を取得する
  const address = addressElement.innerText;

  // 住所が空の場合は何もしない
  if (address.length === 0) {
    return;
  }

  // 住所をクリップボードにコピーする
  try {
    navigator.clipboard.writeText(address);
    alert("住所をコピーしました");
  } catch (error) {
    alert("コピーに失敗しました");
  }
}
clipboard.svg
<!--?xml version="1.0" encoding="utf-8"?-->
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 256px; height: 256px; opacity: 1;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#4B4B4B;}
</style>
<g>
	<rect x="115.774" y="335.487" class="st0" width="194.387" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="115.774" y="260.208" class="st0" width="194.387" height="18.527" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="115.774" y="184.862" class="st0" width="194.387" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<rect x="218.582" y="109.576" class="st0" width="91.58" height="18.588" style="fill: rgb(75, 75, 75);"></rect>
	<path class="st0" d="M385.112,396.188V39.614c0-2.294-0.197-4.603-0.592-6.768C381.3,14.19,365.006,0,345.438,0H184.686
		c-11.561,0-22.598,4.603-30.741,12.747L53.637,112.986c-8.151,8.22-12.747,19.249-12.747,30.818v252.384
		c0,21.802,17.806,39.607,39.683,39.607h264.864C367.308,435.795,385.112,417.99,385.112,396.188z M170.634,27.529v89.074
		c0,9.662-3.745,13.399-13.339,13.399H68.222L170.634,27.529z M63.163,396.188V149.775h106.02c3.486,0,6.768-0.85,9.655-2.362
		c4.079-2.036,7.361-5.324,9.328-9.328c1.519-2.894,2.302-6.115,2.302-9.526V22.272h154.97c7.156,0,13.331,4.33,15.959,10.574
		c0.92,2.104,1.376,4.337,1.376,6.768v356.574c0,9.518-7.748,17.342-17.335,17.342H80.574
		C70.98,413.53,63.163,405.706,63.163,396.188z" style="fill: rgb(75, 75, 75);"></path>
	<path class="st0" d="M431.488,76.205h-26.732l1.375,22.272h25.356c9.594,0,17.349,7.748,17.349,17.342v356.573
		c0,9.519-7.755,17.342-17.349,17.342H166.562c-7.163,0-13.339-4.406-15.968-10.581c-0.85-2.097-1.374-4.33-1.374-6.761V456.89
		h-22.272v15.503c0,2.294,0.198,4.589,0.593,6.761c3.22,18.588,19.515,32.846,39.022,32.846h264.926
		c21.877,0,39.622-17.805,39.622-39.607V115.82C471.11,93.943,453.365,76.205,431.488,76.205z" style="fill: rgb(75, 75, 75);"></path>
</g>
</svg>

Discussion