Googleフォームは超有能
「Googleフォーム」は、チェックボックスは普通に必須にできるし、最近は項目に画像の追加もできるので、使い勝手としては悪くない。むしろ十分。めちゃくちゃ有能。
しかし、「Googleフォーム」の使用が前提で 「Googleフォーム」にはない機能要求がある場合は、どんな工夫ができるのか。
Googleフォームでやりたいこと
商品の問い合わせと仮定した場合、回答側としては、商品名が入っていた方がスムーズなやり取りができる。
商品詳細ページから遷移した時は、商品名やIDが自動挿入されていると便利だなあ…。
ということで、Googleフォームに対してやりたいこと。
- 商品IDを自動挿入したい
- 商品IDはURLから取得したい
- 商品IDは編集不可にしたい
あらかじめ入力済みにしておくことはできる
『1』については、固定テキストなら「事前入力したURLを取得する」機能が使える。
固定なので、商品別に作りたくなると、ちょっと死んじゃうかもしれない。
動的な挿入はこちらが参考になる → GoogleフォームにURLパラメータ使って初期値を指定する方法 | なかちょんブログ
ほぼやりたいことだが、今回は参考にしなかった1。 いつかやる。
↓やりました。
「GoogleフォームにURLパラメータ使って初期値を指定する方法」はWPのプラグインだったので、こちらはjQueryで対応。
運用でカバーは絶対にイヤ ♪
人間には頑張り切れる事とできない事がある
商品IDをどこかから取得したいし、自動挿入された商品IDは誤って削除できないようにしたい。
これらの希望をを叶えるためには、ごにょごにょしないと無理(2022年5月現在)。
…ということで、3つの要件を同時に満たすために、フォーム部分はHTMLを別に作る事にする。
Googleフォームをカスタマイズする記事はかなりの数存在するので、それらを参考にすれば難しいことではない(はず)。
Googleフォームをカスタマイズする
GASを使ってスプレットシートからフォームを作る方法もあるようですが、今回は通常通りに作ります。
作業の手順は下記の通り。
- Googleフォームを作る
- Googleフォームから
form action
とname
を写し取る - フォームと送信完了画面のHTMLを作る
- フォームHTMLからGoogleフォームにデータが送信できているか確認
- フォームHTMLにごにょして送信完了画面に遷移させる
送信完了画面はGoogleフォームのデフォルトを使うこともできるし、フォームデザインに合わせたいということであれば、HTMLを用意すれば良いと思います。
Googleフォームを作る
Googleフォームはこんな感じ → フォームをカスタマイズしたい!
項目はなんとなくなので、大した意味はありません。
ここで重要なのはform action
と各項目のname
をゲットすること。
<form action="https://docs.google.com/forms/u/0/d/e/xxxxxxxxxx" method="post" id="contact">
GoogleフォームとHTMLを紐づけた後、各項目を紐づけます。
name
はentry.xxxxxx
という感じで数字との組み合わせです。
Webデベロッパーツールで検索すればいいよというアドバイスが多いのですが、それでも見つけるのに骨が折れる(自分調べ)。
ひとつづつ項目を検索するのは面倒なので、実際に選択・入力してname
を確認します。
記事内にGoogleフォームのURLがあるので、モザイク処理する甲斐もないんですが、まあ一応。
今回はチェックボックスと連動する入力フォームがあるので、『あなたが習得したいのは?(複数回答可)』の項目は「その他」を選択してテキストを入力しました。
この部分は他のname
やvalue
と違って書き方が特殊なので、あえて選びます。
<div class="customForm_desc_comments">
<input type="checkbox" id="otherFavorite" name="entry.xxxxxx" value="__other_option__">
<label for="otherFavorite">その他</label>
<div class="customForm_desc_comments_input">
<input type="text" name="entry.xxxxxx.other_option_response">
</div><!-- /.customForm_desc_comments_input -->
</div><!-- /.customForm_desc_comments -->
チェックボックスの選択項目のname
は同じ内容、value
は項目名ですが、チェックボックスとそれに連動する項目の書き方はこんな感じ。
この辺りは、Googleフォームのお作法通りに写し取ると間違いありません。
このフォームは「結果の概要を表示する」をONにしているので回答結果も見えちゃいます。
今のところテスト用にオープンにしていますが、回答にはくれぐれもご注意を
HTMLでフォーム画面を作る
バリデーションが走るようにする
最近のHTMLはrequired
を書くと簡単なバリデーションが走ります。
<form action="#" method="post">
<dl class="customForm">
<dt class="customForm_term">
あなたの性別を教えてください<span class="customForm_term_requiredMark">*</span>
</dt>
<dd class="customForm_desc customForm_desc-gender">
<input type="radio" id="man" name="" value="男性" required>
<label for="man">男性</label>
<input type="radio" id="woman" name="" value="女性">
<label for="woman">女性</label>
<input type="radio" id="etc" name="" value="その他">
<label for="etc">その他</label>
</dd>
</dl>
<input id="submit" type="submit" value="送信する">
</form>
エラーテキストはブラウザによって微妙に違います。
チェックボックスにも使えますが、いくつかある項目の最低1つを選ぶという内容には使えません。
今回の場合、『あなたが習得したいのは?(複数回答可)』にはrequired
によるバリデーションがかけられないので、これ以上の仕込みをせずに運用すると次のような挙動になります。
バリデーションが走らない時に起こること
- 完了画面はGoogleフォームのもの → Googleフォーム本体に遷移してエラーが表示される
- 完了画面はカスタマイズしたもの → 正常送信された風に見える
ウルトラスーパー完全無欠の見栄っ張りでもない限り、『1』のようにGoogleフォームに遷移しちゃっても「(無料で使っているし)まあ仕方ないか」と諦めもつく(商品IDは取れていない可能性はある)。
対して『2』はとってもまずい。
正常に送信された風に見えるだけで、実際は送信されていません。
これを防ぐために、HTMLのrequired
は使わず、jQuery Validation Engineを使います。
そう言えば、↑では細かい指定方法は書いていなかったのだった。
<dt class="customForm_term">
あなたが習得したいのは?(複数回答可)<span class="customForm_term_requiredMark">*</span>
</dt>
<dd class="customForm_desc customForm_desc-leastFavorite">
<div class="customForm_desc_wrap customForm_desc_wrap-favorite">
<input type="checkbox" id ="favorite01" name="entry.xxxxxx" value="CSS" class="validate[minCheckbox[1]]">
<label for="favorite01">CSS</label>
<input type="checkbox" id ="favorite02" name="entry.xxxxxx" value="HTML" class="validate[minCheckbox[1]]">
<label for="favorite02">HTML</label>
︙
<input type="checkbox" id ="favorite21" name="entry.xxxxxx" value="この中にはない" class="validate[minCheckbox[1]]">
<label for="favorite21">この中にはない</label>
<div class="customForm_desc_comments">
<input type="checkbox" id="otherFavorite" name="entry.xxxxxx" value="__other_option__" class="validate[minCheckbox[1]]">
<label for="otherFavorite">その他</label>
<div class="customForm_desc_comments_input">
<input type="text" name="entry.xxxxxx.other_option_response" class="validate[condRequired[otherFavorite]]" disabled="disabled">
</div><!-- /.customForm_desc_comments_input -->
</div><!-- /.customForm_desc_comments -->
</div><!-- /.customForm_desc_wrap -->
</dd><!-- /.customForm_desc -->
複数のチェックボックスから選択するので、n以上選択するを必須にします。
今回はひとつ以上でOKなので、class="validate[minCheckbox[1]]"
という書き方をしています。
maxCheckbox
という指定もできるので、選択できる最大数もいける。
チェックボックスと連動するテキストフォームに対しては、チェックボックスのid="otherFavorite"
を使い、class="validate[condRequired[otherFavorite]]"
という感じで関連づけます。
小回りが効くプラグインなので、設定方法については公式ドキュメントをどうぞ。
URLからパラメータを取得してフォームに自動挿入する
URLに商品IDのパラメータを付け、その値を取得してフォームに自動挿入します。
今回はURLに?parameters=ItemID2022
というパラメータを付けます。
この部分はユーザに編集されたくないのでreadonly
を付けます。
<dt class="customForm_term">
送信されるパラメーター
</dt>
<dd class="customForm_desc">
<input type="input" id="inputParam" name="entry.xxxxxx" value="" readonly>
</dd>
const param = location.search //URLからパラメータを取得する
document.getElementById("inputParam").value = param.replace('?parameters=','');
jQueryを使っているのでこんな感じです。
input id="inputParam"
があるフォームに挿入されます。?parameters=
が邪魔なのでreplaceを使って削除しています。
カスタマイズHTMLの確認 / パラメータ:ItemID2022 (外部サイト)
日本語にしたい時は、取得する際にデコードすればいけます。
const param = location.search //パラメータを取得する
const paramDecode = decodeURI(param); //パラメータをデコードする
document.getElementById("inputParam").value = paramDecode.replace('?parameters=','');
カスタマイズHTMLの確認 / パラメータ:商品名は日本語(外部サイト)
自動挿入したパラメータを見せたくない時はinput type="input"
からinput type="hidden"
にします。
レイアウトから要素を除去してしまうdisplay: none;
はデータが送信されないので使いません。
display - CSS: カスケーディングスタイルシート | MDN
今回はパラメータは1つですが、複数つけることも可能。
ここまで作って、送信テストをしてみます。
Goggleフォームの送信完了画面が表示され、送信したデータが蓄積されていればOKです
送信完了画面もオリジナルにしたいよ、という場合は次に進む。
送信完了画面を作る
この記事用に起こしているので、非常に簡素なものです。
結果をオープンにしているので、そのリンクがついてるだけ。
<main class="wrap">
<article class="contents">
<h1 class="contents_title">
フォームをカスタマイズしたい! 挙動テスト
</h1>
<div class="thanks">
<p class="thanks_desc">
入力内容を送信しました!
</p>
</div>
<input type="button" class="contents_btnClose" value="結果の概要を表示する" onClick="window.open('https://docs.google.com/forms/d/xxxxxx')">
</article>
</main>
データを送信しつつ、任意の場所に遷移するので、フォームのHTMLには次のようなコードを書きます。
まずは<body>
直下に<iframe>
を追加
<body ontouchstart="">
<iframe name="hidden_iframe" id="hidden_iframe" style="display:none;" onload="if(submitted) {window.location='complete.html';}"></iframe>
<main>
<article class="contents">
<h1 class="contents_title">
フォームをカスタマイズしたい!
</h1>
<div class="contents_wrap">
<p class="contents_desc">
Googleフォームをカスタマイズする - フォームに管理用のパラメータを自動挿入する(Qiita記事)用に作りました。<span class="contents_desc-note">※</span>
</p>
︙
form
タグを下記のように変更
- <form action="https://docs.google.com/forms/u/0/d/e/xxxxxx" method="post" id="contact">
+ <form action="https://docs.google.com/forms/u/0/d/e/xxxxxx" method="post" id="contact" target="hidden_iframe" onsubmit="submitted=true;">
JavaScriptも追加
const submitted = false;
これで任意の送信完了画面が表示されるようになります。
つまづいたところ
この記事の元ネタを制作していた時、ここまで作るのに執拗に送信テストを繰り返していました。
はっきりした数は覚えていませんが、内容をさまざまに変えて、ひとりで軽く300は送信したはず(3桁蓄積毎にデータ削除を数回繰り返した)
テスト中につまづいたところがいくつかあったので、メモがわりに記載。
正直凡ミスの数々でしかない。
送信できてるはずなのにデータが蓄積されない
- 必須項目にバリデーション用のCLASSを振っていなかった
- なぜかGoogleフォームが編集途中になっていた
- entry.xxxxの番号が間違ってる
『1』はただの実装抜け漏れ、『2』『3』は制作中に項目の増減・修正等が発生して、GoogleフォームとローカルのHTML制作を行ったり来たりすると起こりやすかった。
『2』については毎回発生するわけではなく、本当にタイミングが分からなかった。「送信データ蓄積されてないなー」と思った時の原因は、ほとんどコレ。
送信完了画面を任意のものにすり替えた後に起こったので、挙動が安定するまではGoogleの送信完了画面を使った方が無駄な労力を使わなくて良いです。
送信完了画面も任意のものを使うと、フォームHTMLとの疎通がうまくできていなくても、しれっと正常送信できた風を装うので。
連打できちゃうどうしよう
裏側はGoogleフォームなので、無視してもいいかな案件(自分で掃除しないからポイ捨てOKと同様の思考 )。
とはいえ、送信完了画面への遷移がちょっとでも遅いと感じると2、待ちきれずに送信ボタンをめちゃくちゃ連打するユーザさんは存在します。
データはその回数分蓄積されるので、数十件送信された同じ問い合わせを弾く作業が発生する事に。
そこでpointer-events
プロパティを使って、送信ボタンを押下したらポインタータップイベントを無効にするようにしました。
ただ、バリデーションが走ってエラーになった時も同じ仕組みが働くので、何かのタイミングで復帰させないと必須項目を入力しても内容を送信できないwww
//連打緩和
$("#submit").on("click", function () {
//「送信」ボタンを押下するとボタンが半透明になり、ポインタータップイベントが無効になる
$(this).css({ opacity: " .5", "pointer-events": "none" });
});
$("form").change(function () {
//フォーム内の要素に変更があるとボタンは透過表示をやめ、ポインタータップイベントが有効になる
$("#submit").css({ opacity: "1", "pointer-events": "auto" });
});
バリデーションが走った後、エラーが表示されている項目を選択または入力するはずなので、フォーム内の要素に変更があった時に、再び送信ボタンを押下できるようにしています。
ヒストリーバックで再送できちゃう
連打にも関連する話で、このままだとブラウザの「戻る」ボタンでフォーム画面に戻れちゃいます。
調べてみたのですが、SafariとFirefoxはヒストリーバックを止めることができるものの、Chromeは完全に止めることができません。
参考URL:Google Chromeは「戻る」ボタンで戻れない悪質なウェブサイトを駆逐する予定 - GIGAZINE
Chromeの場合は悪質なウェブサイトを駆逐するという理由でそのような仕様になっているようです。
まあ、悪いことする人たちが一部にいるので仕方ないですね。ほんとやめてほしい。
ただ、不完全ながらヒストリーバックを止めることはできます。
参考:Google Chromeではhistory APIを使った戻るボタンの制御ができない | 11:30 AM
chromeの仕様を詳細に書くと、「ページ読み込み直後に、ブラウザバックでのpopstateイベントが発火しない」ということみたいです。
ユーザーがクリックなどの能動的なアクションを一度でも起こした後であれば発火します。
なので、ページに飛んできたユーザーに一番最初にボタンを押してもらうようなUIを作れればこの問題は回避できるかもしれません。
こちらの追記通り、発火したりしなかったりします。
私がテストした時はほとんど発火しましたが、使用している端末などの環境次第でヒストリーバックができちゃったりします。
//ヒストリーバックを止める
history.pushState(null, null, location.href);
$(window).on('popstate', function(){
history.go(1);
});
完全には止められないので、もう一つ設定を入れる事にします。
<form action="https://docs.google.com/forms/u/0/d/e/xxxxxx" method="post" id="contact" target="hidden_iframe" onsubmit="submitted=true;" autocomplete="off">
autocomplete="off"
を追加して自動補完機能を無効にします。
この設定を入れたので、ヒストリーバックで戻ってもフォーム内に情報が残りません。
ユーザにとって利便性があり、デフォルトでできることを止めるというのは悪手に近いですが、いたずら防止という視点で考えると自動補完機能の無効に軍配が上がります。
住所や電話番号の入力には便利ですが、そもそも、細かい個人情報の入力が必要になった時点でGoogleフォームは使用しない選択になると思います。
ちなみに、Googleフォームも送信完了画面からヒストリーバックが可能ですが、戻ってもフォーム内に情報は残りません。
HTMLを置くサーバを準備できないとき
Googleフォームのカスタマイズをする場合、HTMLを置くサーバをどこかに持っておく必要があります。
ただ、Webアプリだとサーバは不要になる様子。
参考:Google Apps Script (GAS)で動的HTMLページを作成する方法!
無料版の場合、ヘッダに下記の文字と利用規約のリンクが表示される。
このアプリケーションは、Google ではなく、別のユーザーによって作成されたものです。
英語では
This Application was created by another user, not by Google.
参考:HTML Serviceで作ったWebアプリの上部に表示されるバナーについて
まあ、その通りなんですけど、若干不穏な感じしませんかねw
Googleフォームを利用するメリット・デメリット
Googleフォーム最大のメリットは、手軽にアンケートや問い合わせフォームを作ることができるという事に尽きると思います。
適切に利用すれば、最大限のメリットを受けることができます。
今回のような管理用の動的パラメータを送信内容に含めたいという事になると、若干のカスタマイズが必要になりますが、スプレットシートでデータの集計も可能なので、小規模だったり、期間限定で使用する内容に対しては十分だと思います。
ただ、Google次第のところが大きく、フォームの仕様変更があった場合、このカスタマイズが使用できなくなる可能性があります。
そのため、小規模・短期利用に留めて、利便性を最大限に享受するのが、つまらない事故を防ぐ事になるかと思います。
参考にしたサイト
- Google Form をカスタマイズして、hidden フィールドを設定する | Monotalk
- Googleフォームを自由にデザインカスタマイズする方法 - Kazuki Yonemoto - Medium
- GoogleAppsScriptを使ってGoogleフォームの画面を自由にカスタマイズする! | まさきのエンジニア図書館
- GoogleフォームにURLパラメータ使って初期値を指定する方法 | なかちょんブログ
- URLに含まれるGETパラメータを取得する | GRAYCODE JavaScript
- Javascript でURLのパラメータを取得する方法 | WWWクリエイターズ
- jQueryでURLとパラメータを取得する方法:location | UX MILK
- JavaScriptでブラウザバックを "ほぼ完全禁止" する方法 | PisukeCode - Web開発まとめ
- ブラウザバックしても今いるページに留まらせて欲しいと言われて三千里 using React router
- Google Chromeではhistory APIを使った戻るボタンの制御ができない | 11:30 AM