Vanilla JSを使用してモーダルを実装しました。モーダル内の規約を全て読み終わったら、チェックボックスにチェックが入る仕様です。
こんにちは。Webコーダーのはるです。
現在、所属している「もりけん塾」でハンズオン課題 に取り組み、レビューをいただいています。
今日は、課題24 についてアウトプットをしていきます。
(前回までのアウトプットは、こちらです。)
実装する過程で学んだことを、学習ノートとして記録していきます。
認識の間違えている箇所がありましたら、お問い合わせからご指摘いただけるとうれしいです。
基本の会員登録画面を作成
今回、取り組んだ課題はこちらです。
【仕様】
- バリデーションはここではなし
- ユーザー名、メールアドレス、パスワードの入力欄と利用規約に関するチェックボックス(画像参照)がある。
- 送信ボタンがあるが振るまいの実装はしないで良い
- 利用規約のテキストを押すと、モーダルが立ち上がり(前回作ったもので良い)、ダミーの利用規約がテキストとして読める。スクロールが一番下に行ったらチェックボックスはcheckedになる。もし開いてもスクロールが下まで行っていなければcheckedはfalseのまま
- checkedがtrueの場合送信ボタンを押下すると別ページの
register-done.html
に飛ぶ register-done.html
は画面のようなテキストになっている(画面は適当で、遷移できていることが分かれば良い。CSSも書かないでも良い)- これ以降の課題でログイン画面が登場する課題にはPRのわかりやすい所にユーザー名とpasswordなど示してください
(もりけん先生のJavaScriptハンズオン課題より)
前回までの課題はこちらです。
マークアップ
今回は、タブ操作は未実装です。
フォーム
<form class="form box">
<h1 class="title">Sign Up</h1>
<div class="form__inner">
<div class="form__item">
<label for="name">UserName</label>
<input type="text" class="form__item-input" id="name" name="name">
</div>
<div class="form__item">
<label for="email">E-mail</label>
<input type="email" class="form__item-input" id="email" name="email">
</div>
<div class="form__item">
<label for="password">Password</label>
<input type="password" class="form__item-input" id="password" name="password">
</div>
<div class="form__check" id="js-checkbox-aria">
<input type="checkbox" name="answer" title="answer" value="利用規約に同意しました" id="js-checkbox" disabled>
<span class="form__check-text"><span class="form__check-link" id="js-checkbox-link">利用規約</span>に同意しました</span>
<p class="form__attention">(規約を読むとチェックが入ります)</p>
</div>
<div class="form__button">
<input type="button" id="js-submit-button" value="Create My Account">
</div>
</div>
</form>
モーダル
<div class="modal js-modal" id="js-modal-area" role="dialog" aria-labelledby="dialogTitle">
<div class="modal__inner" js-modal-inner">
<button class="modal__close-button" id="js-modal-close-button" aria-label="Close"><img src="./img/icon-close.svg" alt="モーダルを閉じる" width="512" height="512" decoding=“async”></button>
<div class="modal__contents" id="js-modal-contents">
<h2 class="modal__title" id="dialogTitle>利用規約</h2>
<p class="modal__text">この利用規約(以下,「本規約」といいます。)は,_____(以下,「当社」といいます。)がこのウェブサイト上で提供するサービス(以下,「本サービス」といいます。)の利用条件を定めるものです。登録ユーザーの皆さま(以下,「ユーザー」といいます。)には,本規約に従って,本サービスをご利用いただきます。</p>
(以下、省略..モーダル内の規約が続きます)
レビューをいただき、<dialog>や<article>についても調べましたが、<div>にrole属性をつけることで対応することにしました。
<dialog>は、カスタムダイアログモーダルを作るときに使用するHTML要素です。
下記のような利点があります。
- デフォルトでdisplay: noneが備わっていて、show()メソッドor showModal()メソッドでopen属性を与えると現れる
- モーダルのクローズボタンに自動的にフォーカスがあたる
- close()メソッドでモーダルを閉じたとき、以前のフォーカスの位置にフォーカスが戻る
- モーダル外の要素をクリック or タブキーで選択できなくなる
- Escキーでモーダルを閉じることができる
- モーダル外をクリックするとモーダルが閉じる
- ::backdrop要素がデフォルトで用意されており、背景のオーバーレイをCSS実装することができる
1から設定すべき項目が元々備わっているので、とても便利だと思いました。
しかし、Can I Useを見ると一部ブラウザや、古いバージョンなどでは未対応でした。(2022/6/22現在)
今回は、モーダルで規約を全部読まないと次に進めない仕様だったため、大事を取って<div>で実装することを決めました。(Polifillあり)
使ってみても良かったのかな、といまだに迷ってる点です。
最終的に、下記を参考に、<div>にrole=”dialog”とaria-labelledby="dialogTitle"
を追加する実装を行いました。
aria-labelledby属性のついた要素と、arialabelledby属性に指定されたID名を持つ要素を結びつける役割をします。
<div class="modal js-modal" id="js-modal-area" role="dialog" aria-labelledby="dialogTitle">
<h2 class="modal__title" id="dialogTitle>利用規約</h2> //ここでidにdialogTitleを指定する
<div role=”dialog”>と”利用規約”というタイトルを関係づけることができました。
MDNには
スクリーンリーダーなどの支援技術では、この属性を使用してドキュメント内のオブジェクトをカタログ化し、ユーザーがドキュメント間を移動できるようにします。
とあります。支援技術に、このカスタムダイアログは”利用規約”が書かれているということを明示することができる、と理解しました。
モーダルの表示/非表示
利用規約のリンクをクリックすると、モーダルが開く実装をしました。(復習)
const checkboxLink = document.getElementById("js-checkbox-link");
const modalCloseButton = document.getElementById("js-modal-close-button");
const openModal = () => document.getElementById("js-modal-area").classList.add("is-active");
const closeModal = () => document.getElementById("js-modal-area").classList.remove("is-active");
//規約のリンクをクリックするとモーダルが開く
checkboxLink.addEventListener("click", openModal);
//モーダルのキャンセルボタンをクリックするとモーダルが閉じる
modalCloseButton.addEventListener("click", closeModal);
//モーダル以外の部分を押すとモーダルが閉じる
document.addEventListener("click", (e) => {
e.target.classList.contains("js-modal") && closeModal();
});
Intersection Observer API
スクロールが一番下に行ったらチェックボックスをcheckedにする仕様部分です。
ここで、初めてIntersection Observer APIを使用しました。
これは、ターゲット要素が、指定した要素のビューポートと交差するのを監視して、交差したら指定のコールバック関数を呼び出すことのできる機能です。
オプションを作成する
const observerOptions = {
root: modalInner,
threshold: 1.0
};
- root : 交差を監視する基準の場所(要素)を指定します。nullにするとブラウザのビューポートが指定されます。
- rootMargin : 規定値は0。root要素周りのmarginを”0px 10px 20px 30px”のように指定できます。今回は0なので省略しました。
- threshold : 規定値は0。ターゲットがどのくらい交差したらコールバック関数を呼ぶかを指定できます。例)1pxでも表示されたらコールバックを呼びたい→ 0、100%表示されたら→1.0、半分表示されたら→0.5
監視する対象を指定
const observerTarget=document.getElementById("js-last-sentence");
コールバック関数を作成
setCheckedAttributeToCheckbox関数を作成しました。
MDNによると、コールバック関数の引数には交差した要素が配列で渡ってきます。
渡ってきた要素のisIntersectingプロパティがtrueであれば、チェックボックスの状態を変化させます。
const checkbox=document.getElementById("js-checkbox");
const setCheckedAttributeToCheckbox = ([entry]) => {
if (entry.isIntersecting) {
checkbox.checked = true;
checkbox.disabled = false;
}
};
分割代入を使うと以下のような処理が行われていることと同じになると解釈しました。
const setCheckedAttributeToCheckbox = entries => { //entriesという交差した要素の配列が渡ってくる
const [entry] = entries; // 配列[entry]にentriesを割り当てる
if (entry.isIntersecting) {
checkbox.checked = true;
checkbox.disabled = false;
}
};
オブザーバーの作成
コンストラクターを呼び、交差のたびに呼び出すコールバック関数と、交差のオプションを渡すことでオブザーバーが作成されます。
オブザーバーにobserve()メソッドで監視対象を紐づけると、Intersection Observer APIのセッティングは完了です。
const observer = new IntersectionObserver(setCheckedAttributeToCheckbox, observerOptions);
observer.observe(observerTarget);
送信ボタンを押すと画面遷移
チェックボックスにチェックが入っているときにフォームの送信ボタンを押すと、画面遷移する仕様の実装です。
preventDefault()で送信ボタンのデフォルトの機能をキャンセルしておきます。
さらにチェックボックスがchekedの時は、別のhtmlへ遷移するようにしました。
submitButton.addEventListener("click", (e) => {
e.preventDefault();
if (checkbox.checked) window.location.href = "./register-done.html";
});
location.hrefの値を設定することで、指定のURLに遷移することができます。
今回のコード
バリデーションは、次の課題で実装していきます!
//
学習に使用している本は、もりけん先生推奨の”JavaScript本格入門”です。
あとがき
さえさん(@sae_prog)、もなかさん(@ruby443n)、まいさん(@mai2022web)レビューいただきありがとうございました!
知らなかった<dialog>や、苦手な分割代入を調べる機会をいただきました。モーダルについて、ここまで掘ったのは初めてでした。次の課題で、バリデーションの実装に挑戦したいと思います。
お時間を割いていただきありがとうございました!
今日は以上です。
//
【もりけん塾で勉強しています】
もりけん先生(@terrace_tec)のHPはこちら