Googleフォームで、ラジオボタン、プルダウン、チェックボックスといった選択肢に対して、定員(人数制限)を設定できる機能があると便利です。
例えば、「松の会」「竹の会」「梅の会」のような選択肢があり、それぞれに定員数が設定されている場合、定員に達した「松の会」が選択肢から自動的に消えることで、以降の申し込みを制限することができます。
このような定員制限機能は、イベントの申し込みなど、人数管理が重要な場面では必須と言えるでしょう。しかし、現在のGoogleフォームには、この機能が標準で搭載されていません。
そこでGAS(Google Apps Script)でうまく対応できないかと考えて、スクリプトを組んでみました。スプレッドシートの関数と組み合わせることで、とても応用の効くものができましたので、ここに投稿します。
それではテストのフォームを作りながら、このGASの使用方法を説明していきます。
テストのフォームとスプレッドシートを作る
フォーム
まず、以下のようなフォームを作ります。

とりあえずラジオボタンの質問を作ってください。
①質問、②選択肢、③説明で覚えてください。
スプレッドシート
フォームをスプレッドシートにリンクしてください。

リンクしたスプレッドシートに2枚目のシート(左から2番目のタブ)を作ります。このシートをGASが参照します。

①フォームの質問
②フォームの選択肢
③フォームの説明
④参照セル~ゼロ"0"より大きい任意の数値
①、②、③は、フォームと同じにしてください。
GAS
次にフォームからGAS(updateItems)を登録します。

次のGASをフォームのエディタにコピペしてください。
- function updateItems(SheetNo){
- //アクティブフォーム
- const form = FormApp.getActiveForm();
- //選択肢シート:SheetNoは必要なら変更する(一番左のシートが0、2番目が1、3番目が2…)
- if(!(SheetNo>1)) SheetNo = 1;
- const itemsSheet = SpreadsheetApp.openById(form.getDestinationId()).getSheets()[SheetNo];
- //シートの選択肢、説明などを配列で取得する
- const lastRow = itemsSheet.getLastRow();
- const itemsList = itemsSheet.getRange(1,1,lastRow,3).getDisplayValues();
- //変数宣言
- let newItems = []; let countItems = 0;
- //説明1を取得
- let helpText = itemsList[0][1];
- for (let i = 1; i < lastRow; i++) {
- //参照セルがゼロでない選択肢を取得する
- if (itemsList[i][2] > 0) {
- newItems[countItems++] = itemsList[i][0];
- }
- // 追加の説明2〜を取得する
- if (itemsList[i][1] != "") {
- helpText += "\n" + itemsList[i][1];
- }
- }
- if (countItems > 0) {
- //フォームの質問、説明、選択肢などを取得する
- const items = form.getItems();
- for (let i = 0; i < items.length; i++) {
- //対象の質問を探して見つけたら…
- if (items[i].getTitle() === itemsList[0][0]) {
- //選択肢のタイプ(ラジオボタン or プルダウン or チェックボックス)を選んで…
- switch (items[i].getType()) {
- //ラジオボタンの選択肢と説明を更新する
- case FormApp.ItemType.MULTIPLE_CHOICE:
- items[i].asMultipleChoiceItem().setChoiceValues(newItems).setHelpText(helpText); break;
- //プルダウンの選択肢と説明を更新する
- case FormApp.ItemType.LIST:
- items[i].asListItem().setChoiceValues(newItems).setHelpText(helpText); break;
- //チェックボックスの選択肢と説明を更新する
- case FormApp.ItemType.CHECKBOX:
- items[i].asCheckboxItem().setChoiceValues(newItems).setHelpText(helpText); break;
- }
- break;
- }
- }
- }
- //選択肢の参照セルが全てゼロなので回答受付を終了する
- else {
- if(form.isAcceptingResponses()) { //UpdateItemsを同じフォームで複数使うための対策
- form.setAcceptingResponses(false);
- //回答受付終了をメールで知らせる
- const formName = form.getTitle();
- const formUrl = form.getEditUrl() + "#responses";
- const mailAdd = Session.getActiveUser().getEmail();
- const mailTitle = "フォーム<" + formName + ">が受付終了になりました。";
- const mailText = "フォーム<" + formName + ">が受付終了になりました。\n" + formUrl;
- GmailApp.sendEmail(mailAdd, mailTitle, mailText);
- }
- }
- return countItems;
- } //Ver1.5.7 for maintenance
↓ ↓ コピペ ↓ ↓ (コピペ後は保存をしてください。)

トリガー
つぎにトリガーを追加します。

イベントの種類の選択は、「フォーム送信時」を選びます。

※承認を求められたら、「Advanced」(あるいは、「詳細」)→「Go to ~~(Unsafe)」(あるいは、「~~に移動」)→「Allow」(あるいは、「許可」)

ここまでで準備ができました。
動作テスト
作成したシート(2枚目)の数値(参照セル:C3)をゼロ"0"にしてください。

フォームを実行(プレビュー)します。

回答後、送信してください。

再度、フォームをプレビューしてください。

参照セルをゼロ"0"にした選択肢の項目が消えていたら、成功です。
実践編
ここまでが、GAS(updateItems)を追加したフォームの基本動作です。
この基本動作をもとに、自分で作るフォームに応用していきます。
既存のフォームを修正変更し、改めて回答を募集する際は、過去のデータが混ざらないよう、リンクしたスプレッドシートとフォームの回答を全削除してください。(以下の①②の作業)
①回答シートの回答行を削除する。

②フォームの回答を削除する

応用例その1
次に、このテストフォームを使った応用例を示してみます。
選択肢シート(2枚目のシート)に、新たな項目と関数を加えています。
(数式で表示:[Ctrl]+@)

テストのフォームよりの回答・送信を(15回)繰り返しました。その時の回答シートです。

この時の選択肢シート(2枚目のシート)には、次のように表示されています。

① =COUNTIF('フォームの回答' !C:C, A2)
フォームとリンクしている"フォームの回答"シートに記録されている"お茶の会"のカウント数(参加者数)
② =E2-D2
定員数(任意)から参加者数を引いた残りの席数
③ =if(C2>0, "", A2 & "は定員に達しました")
残りの席数が"0"以下のとき、"お茶の会は定員に達しました"と表示
この時のフォームのプレビューです。

お分かりでしょうか?
回答の選択項目をカウントして、定員数に達した項目が選択肢から消えています。
また、その消えた項目の旨を説明にて表示しています。
このように、選択肢シート(2枚目のシート)に関数と組み合わせることで、自分の作るフォームの質問の選択肢に応用の幅が広がります。
その他、説明
すべての項目が定員に達したら...
選択肢の参照セル(④C列のセル)がすべてゼロ"0"以下の値になると、強制的に回答受付終了となるようにGASに組まれています。同時に、フォームのオーナーへ回答受付終了のメールを送ります。


選択肢のタイプ
選択肢のタイプは、ラジオボタン、プルダウン、チェックボックスの3種類があります。
GASで自動的にタイプを判断しますので、自由に選んでください。
選択肢の項目が多い場合は、プルダウンの方が見た目も使い勝手も良いですね。
シートタブの位置
選択肢シートのタブの位置(左から2番目)を替えたいときは、UpdateItems(SheetNo)の引数を使ってください。引数SheetNoは、タブの位置が3番目は"2"、4番目は"3"…です。
その他、応用例
updateItemsを使った応用例を作ってあります。
ご参考になりましたら幸いです。
既存のフォームを修正変更し、改めて回答を募集する際は、過去のデータが混ざらないよう、リンクしたスプレッドシートとフォームの回答を全削除してください。(以下の①②の作業)
①回答シートの回答行を削除する。

②フォームの回答を削除する

応用例その2(残席数をフォームに表示する)
応用例その3(残席数をフォームの選択肢に表示する)

応用例その4(1~3人の申込みにも定員のある選択肢で対応)
応用例その5(チェックボックスで定員のある選択肢に対応)
応用例その6(定員に申込締切日を追加)
応用例その7(複数の質問の選択肢に定員を設定)
これまでの応用例は一つの質問の選択肢だけの対応ですが、この応用例7を使うことで、複数の質問の選択肢に定員を設定できます。
フォームの回答者数で制限する ~ multiReplyの紹介
updateItemsは、「質問の選択肢」ごとに制限(定員、人数制限)を設けられますが、multiReplyは単純にフォーム全体の回答者数に制限が設けられます。さらに、返信メールにも対応していますので、簡単なフォームであればこれで十分かもしれません。
Googleフォームでとにかく簡単に申し込み人数制限を設定したい方はこちら
おわりに
GASで複雑な処理を行うスクリプトを組むと、様々なフォームに応用させるのが難しくなります。GASの処理は単純で、スプレッドシートの関数で応用できるのが私の好みです。フォームに自分のアイデアを採用するためテストする時、関数はトライ&エラーが簡単ですからね。ちなみに今(執筆時)、選択肢の項目に残数を表示する方法を思いついたので試しています。(済)
以上、このGASが皆様のフォームの開発に役立つことを願いまして、それではまた。
追記、google公式サイトのApps Scriptリファレンスを見ていると、今まで見落としていたgetTypeを見つけました。
これで、ラジオボタンとプルダウンを自動で区別できるようになりました。よかったよかった^o^(Ver.1.3.0)
追記、チェックボックスへの対応も問題ないことが分かりましたので、GAS(updateItems)へチェックボックスも追加しました。(Ver.1.4.0)
追記、UpdateItemsの複数同時使用に対して、複数の終了メールに対策しました。(Ver.1.4.5)
追記、GASの仕様変更によるエラー対応のため、コードに変更を加えました。(Ver.1.5.5)
今までは.setHelpTextはセクション毎に設定できていましたが、
例) Item.setHelpText('文字列');
2024/10月以降のGASのアップデート(?)から、セクションのタイプ毎にキャストする必要があるようです。
例) Item.asMultipleChoiceItem().setHelpText('文字列');
参考) https://support.google.com/docs/thread/300175092?hl=en
情報をいただいた二月様、たくさん様に感謝です。m(_ _)m
バグ等ありましたら、お知らせいただくと幸いです。
コメント
みわこ様
ご報告いただき、ありがとうございます。
また、multiReplyも試していただき、感謝いたします。このスクリプトは大幅な改修後、動作実績がほとんどない状態です。そのため、いただいたご報告をもとに、もう一度動作テストを行ってみたいと思います。
改良版が完成しましたら、ぜひ再度お試しいただけますと幸いです。
一行スクリプトがお役に立てたとのこと、私も嬉しい限りです。シンプルなものほど需要があるのかもしれませんね。
今度、この件を記事にしてみるのも良さそうですね。
ご丁寧にお返事くださり、ありがとうございます。
素早い「別の回答を送信」クリックに対しては、この問題を完全に防ぐことが難しい状況である旨、承知いたしました。
また、別のGASのご紹介も感謝いたします。
早速multiReplyを試してみたのですが、私の操作がどこか間違っているようで、
2枚目シートのB3セルが0になってもフォームが閉まりませんでした。
また別の眠くない日に、改めてチャレンジしてみようと思います。
シンプルな1行のGASは、試してみたら動いてくれました!
スプレッドシートとの連携が不要で、嬉しいです。
まさに私が必要としていたものです。本当にありがとうございます。
ただ残念ながら、このGASで2回試行してみましたが、
「別の回答を送信」問題は、クリアできなかったことをご報告させていただきます。
(わざと、素早くクリックしてみました!)
今後は、この1行のGASを利用させていただこうと思います。
この度は、本当にありがとうございました。
いえいえ、こちらこそ詳細な使用報告をいただき、心より感謝申し上げます。
このようなご報告が、私にとって最もありがたいものです。
前回、「選択肢の書き換え」処理に時間がかかる問題の対応に、処理中に「回答の受付終了」に切り替える方法を試しました。
しかし、この「回答の受付終了」にする処理にも2~3秒ほどの時間がかかることが分かりました。
そのため、「選択肢の書き換え」処理だけでなく、「回答の受付終了」に切り替える処理にも時間がかかることから、非常に素早い「別の回答を送信」クリックに対しては、この問題を完全には防ぐことが難しい状況です。
この点につきましては、使用上の注意事項としてご理解いただければ幸いです。
また、「全申込者の人数をカウントし、定員数に達した場合にフォームを閉じる」機能に適したGASとして、multiReplyを作っております。
https://terihat.com/gas-multireply/
もしお試しいただけたら嬉しく思います。
さらに、ご希望のシンプルなGASも1行でできます。例えば、10人の定員で設定する場合は以下のようなコードになります。
function myFunction() {
FormApp.getActiveForm().setAcceptingResponses(FormApp.getActiveForm().getResponses().length < 10); }
この定員数10の値はお好きな数値に変更可能です。
この方法ではスプレッドシートとの連携が不要なため、もしかすると素早いクリックにも対応できる?かもしれませんね。
どんなご報告でもいただけましたら幸いです。
お返事が遅くなり、大変失礼いたしました。
解決方法を考えてくださったり、
回答の受付を一時停止する処理を試してくださったり、
本当にありがとうございました。
図々しくも、追加で質問&要望をお伝えさせていただけますでしょうか。
フォームの「選択肢の書き換え」処理に2〜5秒かかるとのことですが、
「選択肢の書き換え」処理をふくまないGASであれば、
この問題は起こらないという理解で正しいでしょうか。
もし正しければ、選択肢の書き換え処理を含まないGASを
作成していただくことは可能でしょうか。
私自身は、選択肢ごとの人数制限などは行わずに、
「全申込者の人数をカウントして、定員数になったらフォームを閉じる」という
シンプルな目的でこのGASを利用させていただいております。
※2枚目のシートのC列に「定員数-申込者数」の数式を入れています。
(このページのGASの趣旨とは違う利用方法で申し訳ありません。)
このシンプルな利用方法のみに必要なGASを作っていただけば、
数秒のタイムラグが起こらなくなるのでしょうか。
全くの素人考えで、知識も無いのに勝手なことを申し上げて申し訳ありません。
的外れな質問であれば、お許しいただけると幸いです。
みわこ様、
動作テストを行っていただき、誠にありがとうございます。さぞご負担の多い作業だったかと存じます。
みわこ様のテスト結果を参考に、私の方でも確認を行いましたところ、いくつかの点が明らかになりましたのでご説明いたします。
このスクリプト(GAS)は、回答の送信後に実行されるもので、その処理内容はフォームの「選択肢の書き換え」です。
しかし、この処理がフォームに反映されるまでに数秒(おおよそ2~5秒)がかかることが確認されました。環境によっては、さらに時間を要する場合もございます。
反映が完了する前にプレビュー(再度質問画面に移る操作)を行うと、正しい選択肢が表示されないことがあるようです。
これまでのテストでは、次のプレビューまで数秒以上の間隔を空けていたため問題は発生しませんでしたが、今回のエラーについては、回答送信後すぐに「別の回答を送信」をクリックしたことで発生した可能性が高いと推測されます。
この問題に対する解決方法として、
・回答送信後、次のプレビューまで数秒以上の間隔を空けること。
・フォームの設定で「別の回答を送信するためのリンクを表示」をオフにすること。
つまり、次のプレビューが迅速に行われることを防ぐことです。
また、スクリプトで対応できないか試みた結果、スクリプト実行中に「回答の受付を一時停止」する処理を試してみました。
しかし、この処理自体が反映されるのにも数秒を要するため、有効な解決策とはならないことが分かりました。そのため、この方法の採用は見送りました。
今回のテストのおかげで、このスクリプトの課題と限界を明確に把握することができました。
この問題はフォーム自体の仕様とも関連していますので、スクリプトの動作上の注意点としてご理解いただければ幸いです。
何かお気付きの点やご要望がございましたら、どうぞお気軽にお知らせください。
今回の件は本当にありがとうございました。