メッセージボックスをつくってみる
アプリを作っていると必要になってくるのは「処理を確認するための問い合わせ」だと思う。
いわゆる「メッセージボックス」を表示するための要素を考えてみた。
UIイメージ
こんな感じのUIがあれば、とりあえずはアプリで使えそう。
HTML での表現
各タイプのメッセージボックスを表示するためのHTMLを表現すると、こんな感じになった。
※最も単純なイメージです。
<!-- メッセージボックスの本体 -->
<div id="evona-msg-box">
<!-- メッセージボックスを覆う背景 -->
<div class="msg-overlay">
<!-- メッセージボックスの表示内容 -->
<div class="msg-panel">
<!-- ヘッダー -->
<div class="msg-header">[ヘッダー内容]</div>
<!-- メッセージ内容 -->
<div class="msg-body">[メッセージ内容]</div>
<!-- フッター -->
<div class="msg-footer">
<button id="evona-msg-box-b1">OK</button>
</div>
</div>
</div>
</div>
| タグ | 用途 |
|---|---|
| メッセージボックスの本体 | メッセージボックスの全体を構成する |
| メッセージボックスを覆う背景 | メッセージボックスを表示している間に他の表示要素を操作できないようにするための「バリア」 |
| メッセージボックスの表示内容 | ヘッダー・メッセージ内容・フッターの親要素 |
| ヘッダー | いわゆるヘッダー |
| メッセージ内容 | 表示内容を表すHTML |
| フッター | いわゆるフッター |
UIの実装は、今までに作成したUIの実装経験で、こんな感じになった。
HTMLをDOMへ追加
export const MessageBoxButtonTypes = {
None: "None",
Ok: "Ok",
OkCancel: "OkCancel",
YesNo: "YesNo",
} as const
export type MessageBoxButtonType = typeof MessageBoxButtonTypes[keyof typeof MessageBoxButtonTypes]
const buttonType: MessageBoxButtonType = MessageBoxButtonTypes.Ok
const parentName = `callerTagName`
function btnName1(): string { return `${parentName}-b1` }
function createUI(htmlContent: string, title: string) {
let btn1 = ``
switch (buttonType) {
case MessageBoxButtonTypes.Ok:
btn1 = `<button id="${btnName1()}">`OK`</button>`
break
}
const btnHTML = `${btn1}`
const footer = `<div class="msg-footer">${btnHTML}</div>`
const wrapper = document.createElement("div")
wrapper.id = this.parentName
wrapper.innerHTML = `
<div class="msg-overlay">
<div class="msg-panel">
<div class="msg-header">${title}</div>
<div class="msg-body">${htmlContent}</div>
${footer}
</div>
</div>
`.trim()
document.body.appendChild(wrapper)
}
イベント登録
const Result: MessageBoxResultType = MessageBoxResultTypes.None
const onB1Clicked = (event: Event) => {
Result = MessageBoxResultTypes.Ok
remove()
}
document.getElementById(this.btnName1)?.addEventListener("click", onB1Clicked, { once: true })
CSS
function applyCss() {
const styleId = `${parentName}-style`;
if (document.getElementById(styleId)) return;
const style = document.createElement("style");
style.id = styleId;
style.textContent = `
#${parentName} .msg-overlay {
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
background-color: rgba(0, 0, 0, 0.6);
display: flex; justify-content: center; align-items: center;
z-index: 10001;
}
`.trim();
document.head.appendChild(style);
}
※「z-index」について
他のダイアログが表示してあるときでも、「メッセージボックスを最前面」にするために、他のダイアログより大きな値を設定します。
後始末
function remove() {
document.getElementById(btnName1())?.removeEventListener("click", onB1Clicked)
document.getElementById(parentName)?.remove()
}
待つことが必要
ここまでの実装で、メッセージボックス表示はできたけど、「待ってくれない」問題が発覚した。
要するに、操作(「OK」をクリックした)をした後、後続の処理を進める必要があるということ。
状況をGeminiに説明すると、「Promise」というのを使えることを教えてもらった。
MDNに説明が載っていましたが、よく理解できなかったけど、要は、
・何かの条件が成立したとき、Promiseに通知すると待ちを解除してくれる
こんな感じらしい。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises
最終形態
Promiseを考慮した実装は、こんな感じ。
const resolver!: (value: MessageBoxResultType) => void
function async showWait(htmlContent: string, title: string | null = null): Promise<MessageBoxResultType> {
remove()
createUI(htmlContent, title === null ? this.title : title, true);
return new Promise((resolve) => {
resolver = resolve
document.getElementById(btnName1())?.addEventListener("click", onB1Clicked, { once: true });
}
}
const onB1Clicked = (event: Event) => {
Result = MessageBoxResultTypes.Ok
remove()
if (resolver)
resolver(Result)
}
※new Promise()、resolve について。
・new Promise()でインスタンスを作成すると、resolveを通知してくれる。そのあと、Promise内で待ちが発生する。
・resolveはコールバックみたいな関数で、呼び出すと待ちを解除してくれる。
つぎは。
表形式のUIを作っていこうと思った。TABLEタグを勉強しながら作ろうと思う。



