お問い合わせ種別によって必須・任意の項目を変更する方法
お問合せフォームで、特定のお問い合わせ種別を選択した場合は特定の入力項目を必須にしたいという場面があるかと思います。例えば、ユーザーが資料請求のお問い合わせ種別を選択した場合は、資料請求のフォーム入力を必須にしたいというような場面です。a-blog cmsではHTMLのタグで入力項目の必須と任意を制御することができるので、JavaScriptを使用して、動的に入力項目の必須と任意を変更することができます。最近こういった処理を初めて実装したので備忘録と情報の整理を兼ねてブログに残しておこうと思います。
今回は簡単な例を紹介するために、a-blog cmsの beginner2020 のテーマに最初から入っているお問合せフォームで実装していきます。また、注意点としてこの実装方法はa-blog cmsのバリデーターブロックでバリデーションを実装することを前提としています。そのため、JavaScriptで実装されたバリデーションと組み合わせている場合は再現しませんのでご了承ください。
フォームの初期状態
beginner2020テーマのデフォルトのフォームを見やすいように少しカスタマイズして、上記のようなフォームを用意しました。デフォルトからの変更点としては、お問い合わせ内容・電話番号・年齢・住所のフォームを削除したことと、名前のフォームを任意にしたこと、お問い合わせ種別をわかりやすいようにラジオボタンに変更したことです。
このフォームで、お問い合わせ種別で「資料請求」を選択したときには、名前の項目のフォームを必須項目に変更するという実装をしていきたいと思います。(実際にはこの例のようなことはないかと思いますが、記事として説明しやすかったので許してください)
また、必須と任意を切り替える名前のフォーム部分のテンプレートは以下のようになっています。
<li class="contact-form-group"> <p class="contact-form-label">お名前<span id="js-form-label" class="label-optional">任意</span></p> <div class="contact-form-control"> <label for="name" class="acms-hide-visually">苗字</label> <input id="name" type="text" name="name" autocomplete="name" class="acms-form-width-full" value="{name}" placeholder="山田"> <input type="hidden" name="field[]" value="name"> <input type="hidden" name="name:v#required" disabled> <input type="hidden" name="name:c" value="KV"> <!-- BEGIN name:validator --> <p class="error-text"><span class="acms-icon acms-icon-attention"></span>名前を入力してください。</p> <!-- END name:validator --> </div> </li>
特に注目してほしい部分は、2点あります。
1点目はお名前の項目のフォームは最初の状態では必須項目にしたくないため、 <input type="hidden" name="name:v#required">
に disabled
属性を追加している点です。HTMLの仕様として、 disabled
属性のついた項目は送信されないようになっているため、結果として現在この「お名前」のフォームは任意のフォームになっています。このdisabledをJavaScriptで操作することで、必須項目と任意項目の切り替えを実装していきます。
2点目はラベルが <span id="js-form-label" class="label-optional">任意</span>
になっています。任意のフォームにはラベルがついていないWebサイトも多いですが、今回はわかりやすいように任意のラベルを作成しました。こちらも必須項目のときには必須のラベルに変更するプログラムを実装していきたいと思います。また、JavaScript側からラベルの要素を取得するために id
要素を追加しています。
JavaScriptの実装
では実際にJavaScriptの実装をしていきます。↓が実装したJavaScriptのコードです。
ACMS.Ready(function() { const triggerEvent = (el, eventName, options) => { let event; if (window.CustomEvent) { event = new CustomEvent(eventName, { cancelable: true }); } else { event = document.createEvent('CustomEvent'); event.initCustomEvent(eventName, false, false, options); } el.dispatchEvent(event); }; const toggleRequired = () => { const radios = document.querySelectorAll('input[name="type[]"]'); // ラジオボタンの要素を全て取得 const label = document.getElementById('js-form-label'); // ラベルの要素を取得 const targets = document.querySelectorAll('input:disabled'); // disabledがついたinput要素を取得 [].map.call(radios, (radio) => { radio.addEventListener('change', (e) => { const { checked } = e.currentTarget; if (!checked) return; const { value } = e.currentTarget; if (value === '資料請求') { [].map.call(targets, (target) => { target.removeAttribute('disabled'); label.classList.remove('label-optional'); label.classList.add('label-required'); label.textContent = '必須'; }); } else { [].map.call(targets, (target) => { target.setAttribute('disabled', 'disabled'); label.classList.remove('label-required'); label.classList.add('label-optional'); label.textContent = '任意'; }); } }); }); [].map.call(radios, (radio) => { triggerEvent(radio, 'change'); }); }; toggleRequired(); });
少しづつ紹介していきます。
const radios = document.querySelectorAll('input[name="type[]"]'); // ラジオボタンの要素を全て取得
const label = document.getElementById('js-form-label'); // ラベルの要素を取得
const targets = document.querySelectorAll('input:disabled'); // disabledがついたinput要素を取得
ここでは必要な要素を取得しています。 targets
に入ってくるのは disabled属性を付けた <input type="hidden" name="name:v#required" disabled>
です。
[].map.call(radios, (radio) => {
radio.addEventListener('change', (e) => {
const { checked } = e.currentTarget;
if (!checked) return;
const { value } = e.currentTarget;
ここでは、ラジオボタンの1つが変更するたびに、変更したラジオボタンの value
属性の値を取得しています。また、ラジオボタンにチェックが付いていなかった場合には関数を return
し、処理を終了させています。これは後ほど出てくる triggerEvent()
でチェックが付いていないラジオボタンの change
イベントが発火したときの対策です。
if (value === '資料請求') {
[].map.call(targets, (target) => {
target.removeAttribute('disabled');
label.classList.remove('label-optional');
label.classList.add('label-required');
label.textContent = '必須';
});
} else {
[].map.call(targets, (target) => {
target.setAttribute('disabled', 'disabled');
label.classList.remove('label-required');
label.classList.add('label-optional');
label.textContent = '任意';
});
}
そして、ここのif文で取得した value
の値が「資料請求」だった場合、targets
として取得した、input要素1つ1つから disabled
要素を削除します。「資料請求」以外だった場合には、targets
として取得した、input要素1つ1つにdisablsed = disabled
を付与します。また、ラベルの付替えもここで行っています。
上記の条件分岐により、お問い合わせ種別による、任意・必須の切り替えを行っています。
[].map.call(radios, (radio) => {
triggerEvent(radio, 'change');
});
最後に上記の部分ですが、この部分ではエラーや、確認画面で修正したいと思ったときに入力画面に戻ってきたときに、change
イベントを発行することで、先程説明した addEventListener
で登録したコールバックの処理を強制的に実行しています。そうすることで、入力画面に戻ってきたときに「資料請求」が選択されていたら名前の項目を必須項目にします。逆に triggerEvent()
の処理がないと入力画面に戻ってきた場合に「資料請求」が選択されているのに、名前の項目のフォームは任意になっているといった動作になってしまいます。
さて実際の画面で見てみても、「資料請求」が選択されているときは、お名前のフォームが必須になるようになりました。
まとめ
実際の案件では、ラジオボタンでなく、チェックボックスで実装したのですが、ブログでは勉強も兼ねてラジオボタンで実装してみようと思って、実際にやってみたのですが、複数選択できるチェックボックスと複数選択できないラジオボタンでは勝手が違い、手間取ってしまいました。また、公式のドキュメントを参考にしたのですが、僕が探した限り、生のJavaScriptで実装しているような例がなかったので、jQuery でなく、生のJavaScriptで実装したい!というような方の助けになれば幸いです。それでは!