2023年8月に記事後半にてCORSについて追記しました
また最新の情報はhttps://shogo-log.com/google-form-ajax/ の方を更新しております。
静的サイトのフォーム実装問題は、これまで数多の駆け出しweb制作者たちを悩ませてきた問題の1つ。
方法は種々あれど、正直PHPファイル触りたくない...サーバーなんて持っての他である。
今回取材班は、ついにそのベストプラクティスを発見することに成功した。
前提条件
- jQuery使用
- 結構無理やり(コーポレートサイトよりかアンケート向き)
ゴール:Googleフォームで静的サイトのフォームを動くようにする
この記事を読んでできるようになること
- Googleフォームとの紐付け
- 送信完了後、同ページに「お問い合わせありがとうございました」のメッセージが出せる
- サンクスページに遷移できる
Googleフォームを作る
HTMLフォームで作る予定のものと、当然同じものを作ります。
input,textareaのnameの値、formのaction先のURLをメモる
chromeの検証機能で探します。
ラジオボタンだけ、各要素の後に最後に1つだけinputがあるのでそこだけメモります。
ここがフォームの送信先になります。
HTMLフォームを作成
<div class="form-wrapper">
<form
action="https://docs.google.com/forms/u/0/d/e/hoge"
id="form">
<div class="box">
<label for="name">お名前</label>
<input type="text" name="entry.396652" id="name" placeholder="お名前" required>
</div>
<div class="box">
<label for="mail">メールアドレス</label>
<input type="mail" name="entry.1934980" id="mail" placeholder="メールアドレス" required>
</div>
<div class="box">
<p class="box">お好きなプログラミング言語</p>
<div class="flex-box"><input type="radio" name="entry.1977612" required value="scala">
<p>scala</p>
</div>
<div class="flex-box"><input type="radio" name="entry.1977612" required value="kotlin">
<p>kotlin</p>
</div>
<div class="flex-box"><input type="radio" name="entry.1977612" required value="basic">
<p>basic</p>
</div>
</div>
<div class="box">
<textarea name="entry.181178" id="" cols="30" rows="10" placeholder="自由記述"></textarea><!-- /# -->
</div>
<input type="submit" class="submit-btn">
</form>
<p class="end-message">お問い合わせありがとうございました</p>
<p class="false-message">送信失敗です</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
ここで、googleフォームと同じ内容のHTMLフォームを作成し、nameとactionの値を合わせます。
- formタグにid="form"→form内のinputのvalueを取得するためid指定(名前は任意)
- jQuery読み込み→Ajax利用のため
この辺りは後々必要になってくるので、記述しておきます。
送信してみる
おおー受け取れてる!!
とここで感動してもいいですが、
いきなりこんなページに飛ばされるのは、ユーザビリティ的に問題があります。
ここからajax実装で、この画面に遷移しないように手を加えていきましょう。
お問い合わせ完了メッセージをだす。
$(document).ready(function () {
$('#form').submit(function (event) {
var formData = $('#form').serialize();
$.ajax({
url: "https://docs.google.com/forms/u/0/d/e/hoge",
data: formData,
type: "POST",
dataType: "xml",
statusCode: {
0: function () {
$(".end-message").slideDown();
$(".submit-btn").fadeOut();
//window.location.href = "thanks.html";
},
200: function () {
$(".false-message").slideDown();
}
}
});
event.preventDefault();
});
});
まずserializeでform内のinputのvalueを取り出しgormDataに代入
その後urlにgoogleフォーム送信先(action)のurl記入
そしてjQueryのメソッドで送信後「完了メッセージ表示」
最後にevent.preventDefault
でsubmitイベントを無効化し遷移させない
という流れ。
送信後完了メッセージをslideDownで表示することができました。
さりげに送信後submit-btnをfadeOutさせているところに、気配りを感じますよね?(二重投票防止)
非同期でもデータ受け取れてます。なんか感動...
サンクスページ遷移
こちらは一瞬です。
statusCode: {
0: function () {
//$(".end-message").slideDown();
//$(".submit-btn").fadeOut();
window.location.href = "thanks.html";
},
window.location.href
で送信後、thanks.htmlに飛ばしています。
css書いてないので、ページ遷移してる感がないですが、一応遷移してますw
こうしてみると、Googleフォームの集計機能すごいっすね...
問題点
### バリデーション
入力必須ぐらいなら、
<input type="text" required>
みたいにrequired使えば
デフォでこのように表示してくれるけど、バリデーションはフロントよりサーバーサイド(PHP)でやるのが鉄板らしいから、どうなんでしょうか。
クロスドメイン・オリジン問題(Cross-Origin Resource Sharing (CORS))
今回の場合、Googleフォームと異なるドメインからフォームを送るはずです。
クロスドメイン・オリジンとは?
あるドメインで読み込まれたHTMLページ(sample.com)から、Javascriptなどブラウザ上で動作するプログラミング言語によって、異なるドメイン(sample2.com)のサービスに接続すること
実は、今回のフォームは送信後にconsoleにエラーメッセージが表示されます。
Google Form以外のサイトから https://docs.google.com/forms/... へ直接POSTするとCORSエラーが発生してしまうのです。
CORSポリシーに沿うと、Access-Control-Allow-Origin
を設定しないといけないのですが、CORSエラーが発生した場合でもFormの内容自体はしっかりとGoogle Formへ送られているので今回は良しとしました。
ステータスコード0で成功、200でfalseの補足
本来は、HTTP 200番台 Success(成功レスポンス)は、クライアントからリクエストがサーバに送られ理解されて受理された状態で、falseではないはずです。
しかし、ブラウザがクロスオリジンのリクエストに対して正しいCORSヘッダーを受け取らない場合、実際のHTTPステータスコード(たとえば200)を返す前にリクエストをブロックします。
この場合、AJAXリクエストのステータスコードとして0が返されます。
ステータスコード 0
:Google FormsにデータをPOSTすると、正常に送信された場合、ブラウザによるCORSポリシーにより、実際のHTTPステータスコードが返される前にレスポンスがブロックされます。
その結果、jQueryのAJAXリクエストのステータスコードとして0が返されることがよくあります。これは、フォームの送信が実際に成功したことを示すものです。
ステータスコード 200
:このコードは通常、HTTPリクエストが正常に完了したことを示します。ただし今回の場合、このコードが使用される具体的な理由は、以下のような場面です。
- Google FormsのURLが変わったか、無効になった場合。
- 送信データのフォーマットや名前が誤っている場合。
- Google Formsが何らかの理由で正常なレスポンスを返しているが、期待されるデータの送信や保存が行われていない場合。
では、そもそものCORS制約の回避には何をすればいいのかと言うと、下記3つのどれかです。
❶サーバーサイドのプロキシを使用する: 自分のサーバーにエンドポイントを作成し、そのエンドポイントにデータをPOSTします。次に、サーバーサイドのコードはGoogle Formにデータを転送します。この方法で、ブラウザは直接Google Formにリクエストを送信するのではなく、自分のサーバーにリクエストを送信します。そのため、CORS制約は回避されます。
❷JSONPを使用する: JSONPはCORS制約を回避する古いテクニックですが、セキュリティ上の懸念があり、今日の多くのシナリオで推奨されていません。また、すべてのサービスやAPIがJSONPをサポートしているわけではありません。
❸CORSヘッダーを設定する: これはGoogle Formのエンドポイントに直接アクセス権を持っている場合にのみ可能です。しかし、Google FormはユーザーにCORSヘッダーの設定を許可していないので、この方法は使用できません。
最も実用的なのは、オプション1(サーバーサイドのプロキシを使用する)です。自分のサーバーにエンドポイントを設定し、クライアントからのリクエストを受け取り、それをGoogle Formに転送することでCORSの問題を回避できます。
ブラウザから直にリクエストを送らなければいいと。
とはいえ、サーバー建てるのがめんどくさいからGoogle Form使ってるねんって感じなんですが。
受託制作での場合は、相手にフォームを作ってもらう必要あり
Googleアカウント情報をもらうのは煩わしいので、相手にフォームまで作ってもらう必要があります。
少し、相手のITリテラシーが必要かもです。
[追記]:メール送信機能の実装方法を書きました。
googleフォーム送信後にメール送信機能を実装する【Google Apps Script】
参考記事
Googleフォームをカスタマイズして導入、Ajax処理を行う
Googleフォームを自在にカスタマイズする
自動返信機能参考記事
Googleフォームをサイトに埋め込み、自動返信メールを送信する(1)