# 送信時の遅延または二重送信

> Formspree Docs · トラブルシューティング · 2024年4月11日

HTMLフォームで「送信」ボタンをクリックすると、「ありがとう」ページにリダイレクトされるまでの短い間、不安を感じた経験があるかと思います。その時間はほんの数秒ですが、「フォームが機能していないのでは？」「クリックは正常に受け付けられたのか？」「インターネット接続が切れた？」といった疑問が生じることがあります。パニックになったユーザーが送信ボタンを再度クリックしてしまい、重複送信が発生することもあります。

この遅延はユーザー体験を損なうだけでなく、2回目のクリックによってFormspreeフォームへのリクエストが重複し（使用量クォータにカウントされます）、送信リストにも重複レコードが作成されてしまいます（後で削除が必要になります）。本ガイドでは、この問題が発生する理由と、短いJSスニペットを使って解決する方法を説明します。

## **なぜこのような問題が起きるのか？**

Formspreeフォームで「送信」ボタンをクリックした後に発生する遅延は、Formspree自体の問題ではなく、Webフォームの仕組みによるものです。処理の流れを簡単に説明すると：ユーザーが「送信」ボタンをクリックすると、ブラウザはフォームデータをバックエンドにHTTPリクエストとして送信します。このリクエストはインターネットを通じてバックエンドに到達しますが、その途中でネットワーク接続の遅延が発生することがあります。

リクエストがサーバーに到達すると、処理が行われます。これには情報の検証、データベースへの保存、確認メールの送信などが含まれます。この処理にも時間がかかりますが、通常はネットワーク転送よりも短い時間です。処理が完了すると、バックエンドは確認レスポンスを返し、ユーザーを「ありがとう」ページにリダイレクトします。

リクエストがサーバーとの間を往復する時間が、ユーザー側での「送信」ボタンクリック後の遅延を引き起こします。さらに、モバイルデータネットワークのような低速な接続では、リクエストの遅延がさらに大きくなります。

遅延の様子は以下のとおりです：

![9Yt3xqi.gif](/images/zendesk/55d2857f77082f83.gif)

## **どのように解決できるか？**

HTTPリクエストの処理時間そのものを短縮することはできませんが、ユーザーの待機体験を改善することは可能です。React（またはNext/Gatsby）ベースのプロジェクトを開発している場合は、[Formspree Reactライブラリ](/articles/working-with-react/the-formspree-react-library/)の`useForm`フックが返す`state`オブジェクトを使用して、フォームが送信中かどうかを確認し、送信ボタンを無効化できます。ただし、バニラJSの場合は、このチェックを自分で実装する必要があります。

そのためには、Webページでフォームが送信されるたびに発火する[フォーム送信イベント](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event)を監視する必要があります。このイベントを受け取ったら、送信ボタンを無効化したり、フォームデータがバックグラウンドで処理中であることをユーザーに伝えるUI要素（スピナーなど）を表示したりします。

> 注意：「送信」ボタンの[クリックイベント](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event)を使って同様のロジックを実装することもできますが、フォームの`submit`イベントの方が、送信ボタンのクリック、フィールド編集中の`Enter`キー押下、`form.requestSubmit()`メソッドによるプログラムからの送信など、あらゆる送信方法をより確実に捕捉できます。

### **送信時にボタンを無効化する**

バックグラウンド処理をユーザーに伝える最もシンプルな方法は、送信ボタンを無効化し、オプションでボタンにホバーした際に待機カーソルを表示することです。見た目は以下のようになります：

![VueV1eF.gif](/images/zendesk/04905806e773197c.gif)

これを実装するには、CSSでボタンの無効状態に対してグレーアウトした背景と`wait`カーソルを設定します。次に、HTMLフォームの`submit`イベントを監視し、`submit`イベントが発生したときに送信ボタンの`disabled`属性を`true`に設定します。

HTMLファイルの例は以下のとおりです：

```html
<html>

<head>
    <style>
        /* Other styles omitted... */
        
        /* 1. Set the background and cursor for the disabled state of the button */
        #submitButton:disabled {
            cursor: wait;
            background: #dfdfdf;
        }

    </style>
</head>

<body>
    <form id="contact-form" name="simple-contact-form" accept-charset="utf-8" action="https://formspree.io/f/{form_id}"
        method="post">
        <label for="full-name">Full Name</label>
        <input type="text" name="name" id="full-name" placeholder="First and Last" required="">
        <label for="email-address">Email Address</label>
        <input type="email" name="_replyto" id="email-address" placeholder="email@domain.tld" required="">
        <label for="message">Message</label>
        <textarea rows="5" name="message" id="message"
            placeholder="Your message here"
            required=""></textarea>
        <input id="submitButton" type="submit" value="Submit">
    </form>

    <!-- 2. Add the following script to listen to the submit event -->
    <script>
        const form = document.getElementById('contact-form');
        const submitButton = document.getElementById('submitButton');
	

        form.addEventListener('submit', function (event) {
	
            // Sets the submit button's disabled attribute as true
            submitButton.disabled = true;
        });
    </script>
</body>

</html>
```

この方法はシンプルで、プラットフォームネイティブのカーソルローダーを利用できます。たとえば、Windowsユーザーには以下のように表示されます：

![mUg5eOm.gif](/images/zendesk/ecc14008d606e832.gif)

モバイルデバイスではカーソルは表示されませんが、送信ボタンはグレーアウトされるため、ユーザーはタップが受け付けられたことを確認でき、再度タップすることを防げます：

![fU5hbNa.gif](/images/zendesk/221f83b7ee10da1b.gif)

### **送信時にボタンを別のUI要素に置き換える**

別の方法として、フォーム送信時に送信ボタンを完全に非表示にして、スピナーなどのUI要素を代わりに表示することもできます：

![Vi8x3Oc.gif](/images/zendesk/0fa8ce7db3c32b21.gif)

このフォームのコードも同様にシンプルです：

```html
<html>

<head>
    <style>
        /* 1. Add the following styles */
        #loader-container {
            width: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        #loader {
            border: 4px solid #e5122e; 
            border-bottom: 4px solid #fff;
            border-radius: 50%;
            width: 16px;
            height: 16px;
            animation: spin 1s linear infinite;
        }
          
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>

<body>
    <form id="contact-form" name="simple-contact-form" accept-charset="utf-8" action="<https://formspree.io/f/{form_id}>"
        method="post">
        <fieldset id="fs-frm-inputs">
            <div id="inputs">
                <label for="full-name">Full Name</label>
                <input type="text" name="name" id="full-name" placeholder="First and Last" required="">
                <label for="email-address">Email Address</label>
                <input type="email" name="_replyto" id="email-address" placeholder="email@domain.tld" required="">
                <label for="message">Message</label>
                <textarea rows="5" name="message" id="message"
                    placeholder="Your message here"
                    required=""></textarea>
                <input type="hidden" name="_subject" id="email-subject" value="Contact Form Submission">
            </div>
        </fieldset>
        <input id="submitButton" type="submit" value="Submit">
        
        <!-- 2. Add the following div tags -->
        <div id="loader-container" >
            <div id="loader" hidden="hidden"></div>
        </div>
        
    </form>

	<!-- 3. Add the following script to listen to the submit event -->
	<script>
	    const form = document.getElementById('contact-form');
	    const submitButton = document.getElementById('submitButton');
	    const loader = document.getElementById('loader')


	    form.addEventListener('submit', function (event) {
	    
	        // Removes the hidden attribute from the loader element
	        loader.hidden = ""
	        
	        // Sets the submit button to be hidden
	        submitButton.hidden = "hidden";
	    });
	</script>
</body>

</html>
```

送信ボタンの直下に2つの`<div>`（入れ子構造）を追加し、ローダーをコンテナ内に中央配置するCSSと、ローダーのアニメーション用キーフレームを設定します。最後に、フォーム送信イベントを監視して送信ボタンを非表示にしつつローダーを表示するJSスクリプトを追加します。

この方法では、ローダー要素のUIデザインをより柔軟に制御できます。また、プラットフォームを問わず一貫したデザインのローダーを表示することが可能です。

> フォーム送信イベントリスナーを追加するスクリプトは、イベントリスナーを設定するためにHTMLのDOMにアクセスする必要があります。そのため、DOMの読み込みが完了した後にのみコードが実行されるよう注意してください。インラインスクリプトを使用する場合は、上記の例のように`</body>`タグの直前に追加してください。外部スクリプトを使用する場合は、`<script>`タグの`defer`属性を使用してHTMLのDOM解析後まで実行を遅延させるか、スクリプト内でフォーム送信イベントリスナーを追加する前に`DOMContentLoaded`イベントを監視するようにしてください。

## **フォームで快適なユーザー体験を提供する**

以上のように、送信ボタンクリック後のフィードバックの遅延や重複送信の問題は、フォーム送信のバックグラウンド処理をユーザーに伝えるUI要素を実装することで簡単に解決できます。本ガイドが仕組みの理解と、すぐに使えるコードスニペットの提供に役立てば幸いです。
