[WEB/LP]【無料】WEBサイトのお問い合わせフォームをGASで実装しよう

LPでお問い合わせフォームGAS

どうも。Kenny(tsujikenzo)です。今日は単発で自分用メモです。 「【無料】WEBサイトのお問い合わせフォームをGASとスプレッドシートで実装!ハマりポイントも解説」 をお送りします。

はじめに

この記事を書く背景

最近、LP(ペライチ、かんたんなWEBサイトなど)を作成する機会が多くなりました。AIによるコーディング技術がかなり高度になっておりますが、問い合わせフォームはまだまだ実装がむずかしいです。

そして、フォームの送信を受け取ったら、リダイレクト処理をすればいいだけだと思ったら、ハマるポイントがあったので、メモしておきます。

やりたいこと

LPやWEBサイトに、以下のようなシンプルなお問い合わせフォームを実装したいです。 

送信ボタンを押すと、スプレッドシートにデータが書き込まれます。

確認メールも、自動で送信されます。

ユーザーには、サンクスページを表示します。(リダイレクト処理) 

※このリダイレクト処理がハマりポイントでした💦

何にハマったのか

GASには、HTMLを返すサービスが提供されています。なので、送信ボタンが押されたら、サンクスページを表示させればいいはずです。

以下は、meta http-equiv=”refresh” タグを使って、0秒後に thankYouPageUrlへリダイレクトするようブラウザに指示するHTMLを返しています。

function doPost(e) {

  // --- 1. リダイレクト先のURLを指定 ---
  const thankYouPageUrl = "https://example.com/thanks.html";

  // --- 2. リダイレクト指示を含むHTMLを生成して返す ---
  const htmlOutput = HtmlService.createHtmlOutput(
    `<html>
      <head>
        <title>処理中...</title>
        <meta http-equiv="refresh" content="0; url=${thankYouPageUrl}">
      </head>
      <body>
        <p>送信完了しました。ページを移動します...</p>
      </body>
     </html>`
  );

  // ブラウザにHTMLを返す(これがリダイレクト指示となる)
  return htmlOutput;

} 

しかし、うまくリダイレクトされません。ブラウザは真っ白な画面のままです。これはGAS側でセキュリティ制約があるのだと思います。(とAIが言ってました)

そこで、次に試したのが、HTML側のJavaScriptを使ってリダイレクトを制御する方法です。

  1. GAS側は処理成功を示すシンプルなテキスト(例: “success”)を返すだけにする。
  2. リダイレクト自体はフォームを送信したページのJavaScriptで行う。

この方法で、最終的にリダイレクト処理を実現できました。具体的なコードは、次のセクションで解説します。

このブログで作成するもの

  • スプレッドシート
  • WEBアプリとしてデプロイしたURL
  • index.html
  • thanks.html
  • script.js

対象読者

ウェブサイトに問い合わせフォームを設置したい個人事業主、中小企業担当者、プログラミング中級者など。FTPサーバーにファイルをアップロードできるスキルが必要です。

スプレッドシートの用意

まず、スプレッドシートを用意します。

目的

フォームから送信されたデータを記録・管理するためのデータベースとして使用します。

手順

Googleドライブで新規スプレッドシートを作成。シート名を分かりやすく変更しておきます。(手順は省略します)

スプレッドシートの1行目にヘッダー行を作成します。現時点では、「送信日時」「お名前」など、仮の値を設定しておきましょう。(後で変更するのでなんでもOKです) 

このヘッダー行は、お問い合わせフォームの見出しと、一致させます(後でやります)。 

ウェブアプリの用意 (GAS: Google Apps Script)

次にGASを書きます。

目的

WEBサイトから送信されたデータを受け取り、スプレッドシートに書き込み、メール通知を行い、処理結果を返すため。

手順1.doPost(e)関数の作成

スプレッドシートのコンテナバインドスクリプトを開きます。

doPost(e) 関数を作成します。おおまかな流れは以下のとおりです。

  1. フォームデータの受け取り
  2. かんたんなバリデーション
  3. スプレッドシートへの書き込み
  4. メール通知
  5. 成功応答(今回のキモ★★★)
/**
 * HTMLフォームからPOSTリクエストを受け取ったときに実行される関数
 * @param {Object} e - イベントオブジェクト。フォームデータは e.parameter に格納される
 * @return {ContentService.TextOutput} - 処理結果を示すテキスト ("success" またはエラーメッセージ)
 */
function doPost(e) {
  // --- [1] フォームデータの受け取り ---
  // e.parameter オブジェクトから、HTMLフォームの name 属性に対応する値を取得
  // || '' は、もしフォームにその項目がなくてもエラーにならないようにするための記述
  const inquiryType = e.parameter.inquiry_type || '未選択'; // お問い合わせ種別 (例)
  const companyName = e.parameter.company_name || '';    // 会社名 (例)
  const contactName = e.parameter.contact_name || '';    // 担当者名 (例)
  const email       = e.parameter.email || '';           // メールアドレス (例)
  const message     = e.parameter.message || '';         // お問い合わせ内容 (例)
  const privacyPolicy = e.parameter.privacy_policy || ''; // 同意チェック (例)

  // --- [2] かんたんなバリデーション ---
  // 最低限、メールアドレスと同意があるかチェック
  if (!email || privacyPolicy !== 'agreed') {
    Logger.log("エラー: 必須項目が不足しているか、同意がありません。");
    // エラーを示すテキストを返す
    return ContentService.createTextOutput("error: Missing required fields or privacy not agreed.");
  }

  // --- [3] スプレッドシートへの書き込み ---
  try {
    const sheetName = "シート1"; // ★書き込みたいシート名を指定
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName(sheetName);

    if (sheet) {
      // appendRow() で新しい行にデータを追記
      // 配列の順番は、スプレッドシートの列の順番に合わせる
      sheet.appendRow([
        new Date(),    // A列: タイムスタンプ
        inquiryType,   // B列: お問い合わせ種別
        companyName,   // C列: 会社名
        contactName,   // D列: 担当者名
        email,         // E列: メールアドレス
        message        // F列: お問い合わせ内容
      ]);
      Logger.log(`データをシート「${sheetName}」に書き込みました。`);
    } else {
      Logger.log(`エラー: シート「${sheetName}」が見つかりません。`);
      throw new Error(`Sheet "${sheetName}" not found.`); // エラーを発生させてcatchへ
    }
  } catch (error) {
    Logger.log("スプレッドシート書き込みエラー: " + error);
    return ContentService.createTextOutput("error: Failed to record data."); // エラー応答
  }

  // --- [4] メール通知 ---
  try {
    const subjectUser = "【株式会社はじめ】お問い合わせありがとうございます"; // ユーザー向け件名
    const subjectAdmin = `【はじめてLP】お問い合わせがありました (${contactName}様)`; // 管理者向け件名
    const adminEmail = "kenny@yahoo.co.jp"; // ★管理者メールアドレス

    // メール本文(共通部分)
    const body = `以下の内容でお問い合わせを受け付けました。\n\n` +
                 `--------------------\n` +
                 `お問い合わせ種別: ${inquiryType}\n` +
                 `会社名: ${companyName}\n` +
                 `お名前: ${contactName}\n` +
                 `メールアドレス: ${email}\n` +
                 `内容:\n${message}\n` +
                 `--------------------\n\n` +
                 `※このメールは自動送信です。\n\n` +
                 `株式会社はじめ \nhttps://hajime.com/`;

    // ユーザーへ自動返信
    if (email) { // メールアドレスがあれば送信
      GmailApp.sendEmail(email, subjectUser, body);
      Logger.log(`自動返信メールを ${email} へ送信しました。`);
    }

    // 管理者へ通知
    GmailApp.sendEmail(adminEmail, subjectAdmin, body);
    Logger.log(`管理者通知メールを ${adminEmail} へ送信しました。`);

  } catch (error) {
    Logger.log("メール送信エラー: " + error);
  }

  // --- [5] 成功応答 ---
  // ★★★ 今回のキモ! ★★★
  // 処理が正常に完了した場合、"success" というテキストを返す。
  Logger.log("全ての処理が正常に完了。'success'を返します。");
  return ContentService.createTextOutput("success");

} // doPost 関数の終わり

10~15行目のフォームの見出し(スプレッドシートのヘッダー行)は、増やしたり減らしたり自由に設定できます。こちらを適宜変更しましょう。

const inquiryType = e.parameter.inquiry_type || '未選択'; // お問い合わせ種別 (例)
const companyName = e.parameter.company_name || '';    // 会社名 (例)
const contactName = e.parameter.contact_name || '';    // 担当者名 (例)
const email       = e.parameter.email || '';           // メールアドレス (例)
const message     = e.parameter.message || '';         // お問い合わせ内容 (例)
const privacyPolicy = e.parameter.privacy_policy || ''; // 同意チェック (例)

同様に、スプレッドシートへ書き込む値や、メール送信する値も、変更可能です。

sheet.appendRow([
  new Date(),    // A列: タイムスタンプ
  inquiryType,   // B列: お問い合わせ種別
  companyName,   // C列: 会社名
  contactName,   // D列: 担当者名
  email,         // E列: メールアドレス
  message        // F列: お問い合わせ内容
]);

変数が一致するように、気をつけてください。

// メール本文(共通部分)
const body = `以下の内容でお問い合わせを受け付けました。\n\n` +
             `--------------------\n` +
             `お問い合わせ種別: ${inquiryType}\n` +
             `会社名: ${companyName}\n` +
             `お名前: ${contactName}\n` +
             `メールアドレス: ${email}\n` +
             `内容:\n${message}\n` +
             `--------------------\n\n` +
             `※このメールは自動送信です。\n\n` +
             `株式会社はじめ \nhttps://hajime.com/`;

手順2.WEBアプリとしてデプロイ

「デプロイ」から「新しいデプロイ」を選択します。種類は「ウェブアプリ」です。

「実行するユーザー:自分」「アクセスできるユーザー:全員」を選択します。

この設定を間違えると、ただしく動作しませんので、お間違えないように。 

デプロイすると、ウェブアプリURLが発行されるので、コピーしておきます。 

index.htmlの用意

次は、htmlです。

目的

ユーザーがフォームを入力するための、WEBサイトの骨組みです。

ポイント

  • action属性に、GASをデプロイして取得したウェブアプリURLを設定します。
  • name属性の値を、GASのe.parameterで受け取るキーと正確に一致させます。 (例: name=”company_name”)
  • 必須項目には required属性を追加しています。

具体的なコード

ポイントはいくつかありますが、コーディングはAIに任せてしまいましょう。

以下のプロンプトを生成AIにぶち込んで、index.htmlを出力してもらいます。プロンプトを作成するときに、フォームの見出しを必要な項目に変更するとラクです。

## 指示:
Google Apps Script (GAS) のWebアプリケーション(doPost)でデータを受け取ることを想定した、株式会社はじめのお問い合わせフォームを主要コンテンツとする、シンプルなランディングページ(LP)の完全なHTMLコード (`index.html`) を生成してください。フォーム送信はJavaScript (`assets/js/script.js`) を使用して非同期で行い、GASからの応答に基づいてサンクスページへリダイレクトします。

## 目的:
GASと連携するHTMLフォーム(JavaScriptによる非同期送信とリダイレクト)の基本的な実装例を作成する。

## 技術仕様:
*   HTML: HTML5、セマンティックタグ使用。
*   CSS: `assets/css/style.css`, `assets/css/reset.css`, `assets/css/responsive.css` を参照する前提で基本的なクラス名を付与。モバイルファースト考慮。
*   JavaScript: 使用する (`assets/js/script.js` を読み込む)。 Vanilla JS (ES6+) で、フォーム送信の `preventDefault()`、`fetch` APIによる非同期POST、GAS応答に基づく `window.location.href` によるリダイレクト、送信中/エラーステータス表示を行う。
*   フレームワーク/ライブラリ: 使用しない。

## LP構成(アウトライン):
1.  ヘッダー: 企業ロゴ (ダミーパス: `assets/images/logo/logo.png`)。
2.  ダミーコンテンツセクション:
    *   見出し (`h2 class="section-title"`) と簡単な説明文 (`p class="section-text"`) を1つだけ配置(内容はダミーで良い)。
3.  お問い合わせフォームセクション (ID: `section-contact`):
    *   見出し (`h2 class="section-title"`): 「お問い合わせフォーム」
    *   リード文 (`p class="section-text"`): 簡単な説明。
    *   `<form>` タグ:
        *   `id="contact-form"`
        *   `method="post"`
        *   `action="YOUR_GAS_WEB_APP_URL"` ← **この部分は変更しないでください**
    *   フォーム項目(すべて `<div class="form-group">` で囲む):
        *   会社名 (`label for="company_name"`, `input type="text" id="company_name" name="company_name" required`)
        *   担当者名 (`label for="contact_name"`, `input type="text" id="contact_name" name="contact_name" required`)
        *   メールアドレス (`label for="email"`, `input type="email" id="email" name="email" required`)
        *   電話番号 (`label for="phone"`, `input type="tel" id="phone" name="phone"`)
        *   お問い合わせ種別 (`label for="inquiry_type"`, `select id="inquiry_type" name="inquiry_type" required` 内に `option` を複数設定、初期値は `disabled selected`)
        *   お問い合わせ内容 (`label for="message"`, `textarea id="message" name="message" rows="5" required`)
    *   プライバシーポリシー同意 (`div class="form-group privacy-policy-agreement"`)
        *   チェックボックス (`input type="checkbox" id="privacy_policy" name="privacy_policy" value="agreed" required`)
        *   ラベル (`label for="privacy_policy"`) 内にポリシーページへの仮リンク (`a href="#"`) を含むテキスト。
    *   送信ボタン (`div class="form-submit"`, `button type="submit" class="button button-primary"`)
    *   送信ステータス表示用 (`p id="form-status" class="form-status"`) ← これが重要
4.  フッター:
    *   コピーライト(年号部分は `id="copyright-year"` を付与)。

## HTML生成時の注意点:
*   完全な `index.html` ファイルとして、`<!DOCTYPE html>`, `<html>`, `<head>`, `<body>` タグを含めてください。
*   `<head>` 内には基本的なメタタグ(charset, viewport, title, description)、faviconリンク、CSSリンク (`reset.css`, `style.css`, `responsive.css`) を含めてください。
*   本文は `<main>` タグで囲み、各セクションは `<section>` タグを使用してください。
*   クラス名は、CSSでのスタイリングを想定して適切に付与してください(例: `.container`, `.section-title`, `.form-group`, `.button` など)。
*   フォームの `action` 属性は `"YOUR_GAS_WEB_APP_URL"` のままにしてください。
*   `</body>` タグの直前に `<script src="assets/js/script.js"></script>` を含めてください。
*   ヒーローセクション等の他のコンテンツは不要です。

以下は、実装予定であるGASのWebアプリケーションです。

###ここから
~GASを貼り付ける~
###ここまで

わたしの環境では、以下のようなhtmlを出力しました。

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>お問い合わせフォーム - 株式会社はじめ</title>
 <meta name="description" content="株式会社はじめへのお問い合わせはこちらのフォームからお願いいたします。">
</head>
<body>
<header class="header">
</header>
<main>
    <!-- 1. ダミーコンテンツセクション -->
    <section class="section">
        <div class="container">
            <h2 class="section-title">ダミーセクション</h2>
            <p class="section-text">
                このランディングページは、お問い合わせフォームの動作を確認するためのものです。以下のフォームより、お気軽にお問い合わせください。
            </p>
        </div>
    </section>
    <!-- 2. お問い合わせフォームセクション -->
    <section class="section section-contact section-light-gray" id="section-contact">
        <div class="container">
            <h2 class="section-title">お問い合わせフォーム</h2>
            <p class="section-text">ご質問、資料請求、その他お問い合わせはこちらからお願いいたします。</p>
            <!-- ▼▼▼ フォーム ▼▼▼ --> <!-- ★★★ 必ず最新のGASデプロイURLに差し替え ★★★ -->
            <form id="contact-form"
                  action="YOUR_GAS_WEB_APP_URL" 
                  method="post">
                <!-- 会社名 (必須) -->
                <div class="form-group">
                    <label for="company_name">会社名*</label>
                    <input type="text" id="company_name" name="company_name" required>
                </div>
                <!-- ご担当者名 (必須) -->
                <div class="form-group">
                    <label for="contact_name">ご担当者名*</label>
                    <input type="text" id="contact_name" name="contact_name" required>
                </div>
                <!-- メールアドレス (必須) -->
                <div class="form-group">
                    <label for="email">メールアドレス*</label>
                    <input type="email" id="email" name="email" required>
                </div>
                <!-- 電話番号 (任意) -->
                <div class="form-group">
                    <label for="phone">電話番号</label>
                    <input type="tel" id="phone" name="phone">
                </div>
                <!-- お問い合わせ種別 (必須) -->
                <div class="form-group">
                    <label for="inquiry_type">お問い合わせ種別*</label>
                    <select id="inquiry_type" name="inquiry_type" required>
                        <option value="" disabled selected>選択してください</option>
                        <option value="資料請求">資料請求</option>
                        <option value="商品について">商品について</option>
                        <option value="お見積り依頼">お見積り依頼</option>
                        <option value="その他">その他</option>
                    </select>
                </div>
                <!-- お問い合わせ内容 (必須) -->
                <div class="form-group">
                    <label for="message">お問い合わせ内容*</label>
                    <textarea id="message" name="message" rows="5" required></textarea>
                </div>
                <!-- プライバシーポリシー同意 (必須) -->
                <div class="form-group privacy-policy-agreement">
                    <input type="checkbox" id="privacy_policy" name="privacy_policy" value="agreed" required>
                    <label for="privacy_policy">
                        <a href="#" target="_blank" rel="noopener noreferrer">プライバシーポリシー</a>に同意する*
                         <!-- ★ aタグの href に実際のポリシーページのURLを設定してください -->
                    </label>
                </div>
                <!-- 送信ボタン -->
                <div class="form-submit">
                    <button type="submit" class="button button-primary">送信 / Submit</button>
                    <!-- 送信ステータス表示用の p タグ -->
                    <p id="form-status" class="form-status"></p>
                </div>
            </form>
            <!-- ▲▲▲ フォーム ▲▲▲ -->
        </div>
    </section>
</main>
<footer class="footer">
    <div class="container">
        <p class="copyright" id="copyright-year">©はじめ. All Rights Reserved.</p>
         <!-- フッターナビ等、必要なら追加 -->
    </div>
</footer>
<!-- ▼▼▼ JavaScript ファイルを読み込む ▼▼▼ -->
<script src="script.js"></script>
<!-- ▲▲▲ JavaScript ファイルを読み込む ▲▲▲ -->
ody>
</html>

メモ帳などにコピペしてindex.htmlとして保存しましょう。

28行目の”YOUR_GAS_WEB_APP_URL”に、先ほどメモしたデプロイURLを貼り付けましょう。

<!-- ▼▼▼ フォーム ▼▼▼ --> <!-- ★★★ 必ず最新のGASデプロイURLに差し替え ★★★ -->
  <form id="contact-form"
    action="YOUR_GAS_WEB_APP_URL" 
            method="post">

Thanksページ

フォームを送信してもらったら、「ありがとうございました。」というページを表示させます。

以下が、シンプルなthanks.htmlページです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>お問い合わせありがとうございます</title>
    <meta name="robots" content="noindex,nofollow">
</head>
<body>
    <h1>ありがとうございました</h1>
    <p>お問い合わせを受け付けました。担当者からの返信をお待ちください。</p>
    <p><a href="戻りたいページのURLを入力する">TOPページに戻る</a></p>
</body>
</html>

メモ帳などにコピペして、thanks.htmlとして保存しましょう。

script.jsの用意 (フォーム送信&リダイレクト処理)

最後に、フォーム送信の挙動を制御するJavaScriptを用意します。

JavaScriptは、以下の2つを担当します。

  • 「送信ありがとうございます。」をブラウザに表示する
  • 上記を表示するために、ブラウザを制御する

目的

  • フォーム送信時のブラウザ標準動作をキャンセルする (preventDefault)。
  • 入力データを非同期 (fetch) でGASに送信。
  • GASからの応答 (“success”) を受け取って、サンクスページへリダイレクト (window.location.href)。
  • 送信中やエラー時のメッセージ表示。

コードを出力するためのプロンプト

上記の目的を達成するための、プロンプトはこちらです。やはりAIにコーディングしてもらいましょう。

## 指示:
以下の仕様に基づき、HTMLフォームの送信を処理し、Google Apps Script (GAS) と連携してサンクスページへリダイレクトさせるための、完全なJavaScriptコード (`script.js`) を生成してください。

## 前提:
*   HTML (`index.html`) には、以下のIDを持つ要素が存在します:
    *   フォーム: `id="contact-form"`
    *   送信ステータス表示用: `id="form-status"` (例: `<p>`)
    *   送信ボタン: `id="contact-form"` 内の `button[type="submit"]`
    *   プライバシーポリシー同意チェックボックス: `id="privacy_policy"`
*   フォームの `action` 属性には、GASのWebアプリURLが設定されています。
*   GASの `doPost` 関数は、処理が成功した場合に `"success"` という**テキスト**を返し、失敗した場合はエラーを示すテキストを返します。
*   サンクスページのURLは `'あなたが表示させる予定のURL。例)http://www.hajime.com/thanks.html'` です。
*   jQueryなどの外部ライブラリは使用せず、Vanilla JavaScript (ES6+) で記述してください。

## 実装する機能:
1.  DOM読み込み完了後に処理を開始 (`DOMContentLoaded`)。
2.  フォーム送信イベントの捕捉: `contact-form` の `submit` イベントを監視する。
3.  デフォルト送信のキャンセル: `event.preventDefault()` を使用する。
4.  クライアントサイドバリデーション: プライバシーポリシー (`privacy_policy`) がチェックされているか確認し、チェックされていない場合は `form-status` にエラーメッセージを表示して送信を中止する。
5.  送信中ステータス表示: 送信ボタンを無効化し、`form-status` に「送信中...」と表示する。
6.  非同期データ送信: `fetch` API を使用して、フォームデータを `FormData` オブジェクトとしてGASのURL (`action` 属性の値) へ `POST` メソッドで送信する。
    *   `mode: 'cors'` を試行する(必要に応じてコメントで `'no-cors'` の可能性に言及)。
7.  GAS応答の処理:
    *   `fetch` の応答をテキストとして取得 (`response.text()`)。
    *   応答テキストが `"success"` (前後の空白を除去し小文字比較) と一致する場合:
        *   コンソールに成功ログを出力する。
        *   `window.location.href` を使用して指定のサンクスページURLへリダイレクトさせる。
    *   応答テキストが `"success"` 以外の場合:
        *   コンソールにエラーログを出力する。
        *   `form-status` にエラーメッセージ(例: 「送信エラーが発生しました...」)を表示する。
        *   送信ボタンを再度有効化する。
8.  通信エラー処理: `fetch` 自体が失敗した場合(ネットワークエラー等)の `catch` ブロックを設け、コンソールにエラーログを出力し、`form-status` に通信エラーメッセージを表示し、送信ボタンを再度有効化する。
9.  要素存在チェック: スクリプトの冒頭で、必要なHTML要素 (`contactForm`, `formStatus`, `submitButton`) が存在するか確認し、見つからない場合にコンソールエラーを出力する。

## 出力形式:
*   完全な `script.js` ファイルのコードとして出力してください。
*   主要な処理ブロックや重要な箇所には、日本語で分かりやすいコメントを追加してください。

具体的なコード

わたしの環境では、以下を出力しました。

/**
 * DOMContentLoadedイベントリスナー
 * HTMLの読み込みと解析が完了した時点で実行される
 */
document.addEventListener('DOMContentLoaded', () => {
    // 開発コンソールにメッセージを出力して、スクリプトが読み込まれたか確認
    console.log('DOM fully loaded and parsed for Expo LP form');

    // --- お問い合わせフォーム処理 ---
    const contactForm = document.getElementById('contact-form');         // フォーム要素
    const formStatus = document.getElementById('form-status');           // 送信状態を表示するp要素
    const submitButton = contactForm ? contactForm.querySelector('button[type="submit"]') : null; // 送信ボタン

    // 必要な要素がすべて存在するか確認してからイベントリスナーを設定
    if (contactForm && formStatus && submitButton) {
        // フォームの 'submit' イベント(送信ボタンクリック時など)を監視
        contactForm.addEventListener('submit', async (e) => {
            // ★★★ 1. ブラウザ標準のフォーム送信動作をキャンセル ★★★
            e.preventDefault();

            // --- 2. クライアントサイドでの簡単な入力チェック ---
            const privacyCheckbox = document.getElementById('privacy_policy');
            if (!privacyCheckbox || !privacyCheckbox.checked) {
                formStatus.textContent = 'プライバシーポリシーに同意してください。'; // エラーメッセージ表示
                formStatus.className = 'form-status error'; // エラースタイル適用 (CSSで定義)
                return; // ここで処理を中断し、送信しない
            }
            // 他の必須項目チェックが必要であればここに追加

            // --- 3. 送信処理開始の準備 ---
            submitButton.disabled = true; // 送信中にボタンを連打できないように無効化
            formStatus.textContent = '送信中...'; // ユーザーに処理中であることを表示
            formStatus.className = 'form-status'; // エラースタイルをリセット

            // --- 4. フォームデータの準備とGASへの非同期送信 ---
            const formData = new FormData(contactForm); // フォーム内の全入力データを取得
            const actionUrl = contactForm.action;       // formタグのaction属性からGASのURLを取得

            try {
                // fetch API を使って、指定したURL(actionUrl)にフォームデータをPOST送信
                const response = await fetch(actionUrl, {
                    method: 'POST',
                    body: formData,
                    mode: 'cors' // CORSモード推奨 (GAS側は通常対応不要)
                                 // もしこれでエラーが出る場合は 'no-cors' を試す
                });

                // --- 5. GASからの応答を処理 ---
                let responseText = '';
                try {
                    responseText = await response.text(); // GASから返されたテキストを取得
                    console.log("Response from GAS:", responseText); // デバッグ用ログ
                } catch (readError) {
                    console.warn("Could not read response body (likely CORS or no-cors issue, proceeding as potentially successful):", readError);
                    if (response.ok || response.type === 'opaque') {
                        responseText = "success"; // 暫定的に success とする
                    } else {
                        throw new Error("Fetch failed or could not read response");
                    }
                }

                // ★★★ 6. 応答内容に基づきリダイレクトまたはエラー表示 ★★★
                if (responseText.trim().toLowerCase() === "success") {
                    console.log('Form submission successful. Redirecting...');
                    // ★★★ サンクスページへリダイレクト! ★★★
                    window.location.href = 'あなたのサンクスページURL'; // ★サンクスページURL確認
                } else {
                    console.error('サーバーからのエラー応答または予期せぬ応答:', responseText);
                    formStatus.textContent = '送信エラーが発生しました。内容をご確認の上、再試行ください。';
                    formStatus.classList.add('error');
                    submitButton.disabled = false; // ボタンを有効化
                }

            } catch (error) {
                // --- 7. fetch 自体が失敗した場合 (ネットワーク接続がないなど) のエラー処理 ---
                console.error('フォーム送信の fetch エラー:', error);
                formStatus.textContent = '通信エラーが発生しました。ネットワーク環境をご確認の上、再試行してください。';
                formStatus.classList.add('error');
                submitButton.disabled = false; // ボタンを有効化
            }
        });
    } else {
        // HTMLに必要な要素が見つからない場合のエラー (開発用)
        if (!contactForm) console.error('フォーム要素 (#contact-form) が見つかりません。');
        if (!formStatus) console.error('ステータス表示要素 (#form-status) が見つかりません。');
        if (!submitButton) console.error('送信ボタン (button[type="submit"]) が見つかりません。');
    }

}); // End of DOMContentLoaded

メモ帳などにコピペして、script.jsとして保存しましょう。

66行目のあなたのサンクスページURLを、正しく書き換えておきましょう。

// ★★★ 6. 応答内容に基づきリダイレクトまたはエラー表示 ★★★
 if (responseText.trim().toLowerCase() === "success") {
    console.log('Form submission successful. Redirecting...');
         // ★★★ サンクスページへリダイレクト! ★★★
       window.location.href = 'あなたのサンクスページURL'; // ★サンクスページURL確認
 } else {
      console.error('サーバーからのエラー応答または予期せぬ応答:', responseText);
      formStatus.textContent = '送信エラーが発生しました。内容をご確認の上、再試行ください。';
   formStatus.classList.add('error');
   submitButton.disabled = false; // ボタンを有効化

この、JavaScriptによるwindow.location.href = ‘あなたのサンクスページURL’;というのが、今回のキモでした。リダイレクト処理を、GASのHTMLサービスではなく、JavaScriptから行う処理です。

アップロード

作成した、以下のファイルをサーバーにアップロードします。

  • index.html
  • script.js

無事に表示されましたでしょうか。

うまく表示されないばあいは、リダイレクトURLや、WEBアプリとしてデプロイしたURLなどを再確認しましょう。

まとめ

以上で、「WEBサイトのお問い合わせフォームはGASとスプレッドシートで実装しよう」をお送りしました。

AIによるコーディングが活発になってきました。誰でもLPが量産できる時代です。

みなさんの夢が叶えられますように

タイトルとURLをコピーしました