JavaScriptによる入力チェック後にreCAPTCHAのトークンを得てSubmitする

フォームにGoogle reCAPTCHAを設置する最もシンプルな実装は下記のようなものです。
（なお、スコア評価が面倒なのでv2のinvisibleを使います）

```html
<script src='https://www.google.com/recaptcha/api.js' async defer></script>
<form id="myform" action="server.php" method="POST">
    <dd>
        <dt>お名前 【必須】:</dt>
        <dd><input type="text" name="name" value="" /></dd>
    </dd>
    <div class="g-recaptcha"
         data-sitekey="GOOGLE_RECAPTCHA_SITE_KEY"
         data-callback="google_recaptcha_onSubmit"
         data-size="invisible">
        <input type="submit" value="送信する" />
    </div>
</form>
```

このコードのフローとしては、下記のようになっていて「JavaScriptによる入力チェック」は入れていません。

- 1.送信ボタンを押す
- 2.reCAPTCHAのトークンを得て、自動的にフォームが送信される


これに入力チェックを追加します。上記のHTML部分はそのままでJavaScriptの実装を追加します。

```javascript
<script
  src="https://code.jquery.com/jquery-3.4.1.min.js"
  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  crossorigin="anonymous"></script>
<script src='https://www.google.com/recaptcha/api.js' async defer></script>
<script type="text/javascript">
// 面倒なのでjQueryに依存するように書いてます。
$(function() {
    $('#myform').on('submit', function (event) {
        if (! check_form()) { return false; }
        return true;
    });
});
// 入力チェック
function check_form() {
    var err = [];
    if ($('#myform input[name=name]').val() === '') {
        err.push('お名前は必須です。');
    }
    if (err.length > 0) {
        alert( err.join("\n") ); // エラーはalert()で出す簡易版
        return false;
    }
    return true;
}
function google_recaptcha_onSubmit(token) {
    $('#myform').submit();
}
</script>
```

フォームのonsubmitイベントハンドラで入力チェックを行い、もしNGだったらフォームを送信しない。入力チェックがOKならSubmitするようになっています。

しかし、入力内容を修正後に再度送信ボタンを押と、イベントが飛びません。

- 1.送信ボタンを押す
- 2.JSの入力チェック→NG<br>
→名前を入れる
- 3.もう一度送信ボタンを押す<br>
→反応しない (2のイベントを受け付けない)


reCAPTCHA側でcallbackは1回しか送信しない仕様になっているからではないかと思うところです。


ではどうするか。


data-callback属性を設定する要素を変えます。

先程のコードでは、送信ボタンの親divにdata-callback属性を設定していました。これを空divにして送信ボタンは自由にします。

```html
<form id="myform" action="server.php" method="POST">
    <dd>
        <dt>お名前 【必須】:</dt>
        <dd><input type="text" name="name" value="" /></dd>
    </dd>
    <div class="g-recaptcha"
        data-sitekey="GOOGLE_RECAPTCHA_SITE_KEY"
        data-callback='google_recaptcha_onSubmit'
        data-size="invisible">
    </div>
    <input type="submit" value="送信する" /> <!-- ←divの外に出した -->
</form>
```

JavaScriptの実装も、onsubmitイベントハンドラはトークンが取得できたのを確認してからSubmitするように変えます。


```javascript
<script
  src="https://code.jquery.com/jquery-3.4.1.min.js"
  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  crossorigin="anonymous"></script>
<script src='https://www.google.com/recaptcha/api.js' async defer></script>
<script type="text/javascript">
// 面倒なのでjQueryに依存するように書いてます。
var google_recaptcha_token = null; // reCAPTCHAトークンがココに入る
$(function() {
    $('#myform').on('submit', function (event) {
        if (google_recaptcha_token !== null) { return true; }
        if (! check_form()) { return false; }
        grecaptcha.execute();
        return false;
    });
});
// 入力チェック
function check_form() {
    var err = [];
    if ($('#myform input[name=name]').val() === '') {
        err.push('お名前は必須です。');
    }
    if (err.length > 0) {
        alert( err.join("\n") ); // エラーはalert()で出す簡易版
        return false;
    }
    return true;
}
function google_recaptcha_onSubmit(token) {
    google_recaptcha_token = token;
    $('#myform').submit();
}
</script>
```

これでフローは下記のようになり、正常に入力チェックとSubmitができます。

- 0.フォームのonsubmitに関数を割り当てておく
- 1.送信ボタンを押す
- 2.onsubmitが実行される
  - 2-1.recaptchaトークンが存在していたらtrueを返してフォームを送信する
  - 2-2.入力チェックでNGなら再入力を促してfalseで返す（トークンは生成しない）
  - 2-3.入力チェックがOKで、トークンが存在していないなら `grecaptcha.execute()` をしてfalseを返す
- 3.`grecaptcha.execute()`より、data-callbackで指定した関数に返ってくるので、トークンを保存してもう一度submitイベントを発火させる<br>
→2-1に遷移するのでフォームが送信される


reCAPTCHAのドキュメントでも **空div** にdata-callback属性を設定しているので、単に私が見誤ってハマっただけなのですけれども……。
https://developers.google.com/recaptcha/docs/invisible


以上、参考になれば幸いです。