8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【javascript】非同期な自作 confirm を作ってみた

Posted at

はじめに

javascript にはconfirmというメソッドが用意されており、制御フローの条件として使用することができます。
ダイアログが表示され、「OK」「キャンセル」を選ぶことで処理を実行したり中断したりできる大変便利な機能です。

  • コード例
confirm
if (confirm("よろしいですか?")) {
  alert("OKが押されました。");
} else {
  alert("キャンセルが押されました。");
}
  • UI

image.png

見ての通り簡素な UI で、選択肢の文言を変えることなどはできません。
そこで自作の confirm 関数を作成してみました。

仕様

  • 任意の文言を受け付ける。
  • 制御フロー内で条件として使用できる。
  • 処理待ちを行う関係で非同期関数とする。

表面上の仕様は至ってシンプルなものになります。
ユーザーの入力を待ち、制御フローの中で条件として使用できる。
組み込みの confirm と同様の使い心地を実現できればそれで OK です。

  • コード例
confirmAsync
if (await confirmAsync("よろしいですか?")) {
  alert("OKが押されました。");
} else {
  alert("キャンセルが押されました。");
}

設計

  • dialog要素にて UI を作成する。
  • confirmAsync 関数を実行することでモーダル形式でダイアログを表示する。
  • OK ボタンをクリックしたらPromise<true>を返却してダイアログを閉じる。
  • キャンセルボタンをクリックしたらPromise<false>を返却してダイアログを閉じる。

ダイアログを自作する場合、一般的にはdiv要素が使われているのではないでしょうか。
今回はあえてdialog要素を使用していこうと思います。

ブラウザーの互換性に一抹の不安が残りますが、Google Chrome で動作するので良しとします。

実装

ダイアログ本体

<dialog id="dialog">
  <p id="message"></p>
  <button id="button-ok">OK</button>
  <button id="button-cancel">キャンセル</button>
</dialog>

image.png

装飾していない生の HTML です。

confirmAsync 関数

自分で作っておいて言うのもアレですが、結構ややこしいのでいくつかの手順に分割して紹介します。

手順①

  • 表示メッセージの受け取り/設定を行う、
  • ダイアログを表示する。
  • 固定でPromise<true>を返却する。
confirmAsync
const confirmAsync = async message => {
  document.getElementById("message").innerText = message;
  document.getElementById("dialog").showModal();

  return new Promise(resolve => resolve(true));
};

手順②

  • OK ボタン、キャンセルボタンにクリックイベントを設定する。
  • OK ボタンがクリックされたらPromise<true>を返却してダイアログを閉じる。
  • キャンセルボタンがクリックされたらPromise<false>を返却してダイアログを閉じる。
confirmAsync
const confirmAsync = async message => {
  document.getElementById("message").innerText = message;
  document.getElementById("dialog").showModal();

  return new Promise(resolve => {
    document.getElementById("button-ok").addEventListener("click", () => {
      document.getElementById("dialog").close();
      resolve(true);
    });

    document.getElementById("button-cancel").addEventListener("click", () => {
      document.getElementById("dialog").close();
      resolve(false);
    });
  });
};

この時点でそれなりに昨日は完成していますが、もう少し工夫していきます。

手順③

  • クリックイベントを括りだす。
  • OK ボタン用、キャンセルボタン用のイベントを作成する。
    (※次の手順で removeEventListener に設定するためです。)
  • OK ボタンがクリックされたらokEventを実行する。
  • キャンセルボタンがクリックされたらcancelEventを実行する。

confirmAsync
const confirmAsync = async message => {
  document.getElementById("message").innerText = message;
  document.getElementById("dialog").showModal();

  return new Promise(resolve => {
    const eventBase = flag => () => {
      document.getElementById("dialog").close();
      resolve(flag);
    };
    const okEvent = eventBase(true);
    const cancelEvent = eventBase(false);

    document.getElementById("button-ok").addEventListener("click", okEvent);
    document.getElementById("button-cancel").addEventListener("click", cancelEvent);
  });
};

手順④

  • OK ボタンからクリックイベントを削除する。
  • キャンセルボタンからクリックイベントを削除する。
confirmAsync
const confirmAsync = async message => {
  document.getElementById("message").innerText = message;
  document.getElementById("dialog").showModal();

  return new Promise(resolve => {
    const eventBase = flag => () => {
      document.getElementById("dialog").close();
      document.getElementById("button-ok").removeEventListener("click", okEvent);
      document.getElementById("button-cancel").removeEventListener("click", cancelEvent);
      resolve(flag);
    };
    const okEvent = eventBase(true);
    const cancelEvent = eventBase(false);

    document.getElementById("button-ok").addEventListener("click", okEvent);
    document.getElementById("button-cancel").addEventListener("click", cancelEvent);
  });
};

ダイアログの実装手順は以上となります。

実行例

あとは実際に呼び出すだけです。
組み込みの confirm を呼び出すときと同様の感覚で実装できます。

const handleClicked = async () => {
  if (await confirmAsync("よろしいですか?")) {
    alert("OKが押されました。");
  } else {
    alert("キャンセルが押されました。");
  }
};
<button onclick="handleClicked()">確認</button>

confirm.gif

装飾など

UI 自体はただの HTML なので、通常通り css で装飾することができます。
以下は Bootstrap を用いた例です。

confirm2.gif

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?