Chapter 18Forms and Form Fields
I shall this very day, at Doctor’s feast,
My bounden service duly pay thee.
But one thing!—For insurance’ sake, I pray thee,
Grant me a line or two, at least.
Forms were introduced briefly in the previous chapter as a way to submit information provided by the user over HTTP. They were designed for a pre-JavaScript Web, assuming that interaction with the server always happens by navigating to a new page.
But their elements are part of the DOM like the rest of the page, and the DOM elements that represent form fields support a number of properties and events that are not present on other elements. These make it possible to inspect and control such input fields with JavaScript programs and do things such as adding functionality to a traditional form or using forms and fields as building blocks in a JavaScript application.
Fields
A web form consists of any number of input fields grouped in a <form>
tag. HTML allows a number of different styles of fields, ranging from simple on/off checkboxes to drop-down menus and fields for text input. This book won’t try to comprehensively discuss all field types, but we will start with a rough overview.
A lot of field types use the <input>
tag. This tag’s type
attribute is used to select the field’s style. These are some commonly used <input>
types
text |
A single-line text field |
password |
Same as text but hides the text that is typed |
checkbox |
An on/off switch |
radio |
(Part of) a multiple-choice field |
file |
Allows the user to choose a file from their computer |
Form fields do not necessarily have to appear in a <form>
tag. You can put them anywhere in a page. Such fields cannot be submitted (only a form as a whole can), but when responding to input with JavaScript, we often do not want to submit our fields normally anyway.
<p><input type="text" value="abc"> (text)</p> <p><input type="password" value="abc"> (password)</p> <p><input type="checkbox" checked> (checkbox)</p> <p><input type="radio" value="A" name="choice"> <input type="radio" value="B" name="choice" checked> <input type="radio" value="C" name="choice"> (radio)</p> <p><input type="file"> (file)</p>
The JavaScript interface for such elements differs with the type of the element. We’ll go over each of them later in the chapter.
Multiline text fields have their own tag, <textarea>
, mostly because using an attribute to specify a multiline starting value would be awkward. The <textarea>
requires a matching </textarea>
closing tag and uses the text between those two, instead of using its value
attribute, as starting text.
<textarea> one two three </textarea>
Finally, the <select>
tag is used to create a field that allows the user to select from a number of predefined options.
<select> <option>Pancakes</option> <option>Pudding</option> <option>Ice cream</option> </select>
Whenever the value of a form field changes, it fires a "change"
event.
Focus
Unlike most elements in an HTML document, form fields can get keyboard focus. When clicked—or activated in some other way—they become the currently active element, the main recipient of keyboard input.
If a document has a text field, text typed will end up in there only when the field is focused. Other fields respond differently to keyboard events. For example, a <select>
menu tries to move to the option that contains the text the user typed and responds to the arrow keys by moving its selection up and down.
We can control focus from JavaScript with the focus
and blur
methods. The first moves focus to the DOM element it is called on, and the second removes focus. The value in document.activeElement
corresponds to the currently focused element.
<input type="text"> <script> document.querySelector("input").focus(); console.log(document.activeElement.tagName); // → INPUT document.querySelector("input").blur(); console.log(document.activeElement.tagName); // → BODY </script>
For some pages, the user is expected to want to interact with a form field immediately. JavaScript can be used to focus this field when the document is loaded, but HTML also provides the autofocus
attribute, which produces the same effect but lets the browser know what we are trying to achieve. This makes it possible for the browser to disable the behavior when it is not appropriate, such as when the user has focused something else.
<input type="text" autofocus>
Browsers traditionally also allow the user to move the focus through the document by pressing the Tab key. We can influence the order in which elements receive focus with the tabindex
attribute. The following example document will let focus jump from the text input to the OK button, rather than going through the help link first
<input type="text" tabindex=1> <a href=".">(help)</a> <button onclick="console.log('ok')" tabindex=2>OK</button>
By default, most types of HTML elements cannot be focused. But you can add a tabindex
attribute to any element, which will make it focusable.
Disabled fields
All form fields can be disabled through their disabled
attribute, which also exists as a property on the element’s DOM object.
<button>I'm all right</button> <button disabled>I'm out</button>
Disabled fields cannot be focused or changed, and unlike active fields, they usually look gray and faded.
When a program is in the process of handling an action caused by some button or other control, which might require communication with the server and thus take a while, it can be a good idea to disable the control until the action finishes. That way, when the user gets impatient and clicks it again, they don’t accidentally repeat their action.
The form as a whole
When a field is contained in a <form>
element, its DOM element will have a property form
linking back to the form’s DOM element. The <form>
element, in turn, has a property called elements
that contains an array-like collection of the fields inside it.
The name
attribute of a form field determines the way its value will be identified when the form is submitted. It can also be used as a property name when accessing the form’s elements
property, which acts both as an array-like object (accessible by number) and a map (accessible by name).
<form action="example/submit.html"> Name: <input type="text" name="name"><br> Password: <input type="password" name="password"><br> <button type="submit">Log in</button> </form> <script> var form = document.querySelector("form"); console.log(form.elements[1].type); // → password console.log(form.elements.password.type); // → password console.log(form.elements.name.form == form); // → true </script>
A button with a type
attribute of submit
will, when pressed, cause the form to be submitted. Pressing Enter when a form field is focused has the same effect.
Submitting a form normally means that the browser navigates to the page indicated by the form’s action
attribute, using either a GET
or a POST
request. But before that happens, a "submit"
event is fired. This event can be handled by JavaScript, and the handler can prevent the default behavior by calling preventDefault
on the event object.
<form action="example/submit.html"> Value: <input type="text" name="value"> <button type="submit">Save</button> </form> <script> var form = document.querySelector("form"); form.addEventListener("submit", function(event) { console.log("Saving value", form.elements.value.value); event.preventDefault(); }); </script>
Intercepting "submit"
events in JavaScript has various uses. We can write code to verify that the values the user entered make sense and immediately show an error message instead of submitting the form when they don’t. Or we can disable the regular way of submitting the form entirely, as in the previous example, and have our program handle the input, possibly using XMLHttpRequest
to send it over to a server without reloading the page.
Text fields
Fields created by <input>
tags with a type of text
or password
, as well as textarea
tags, share a common interface. Their DOM elements have a value
property that holds their current content as a string value. Setting this property to another string changes the field’s content.
The selectionStart
and selectionEnd
properties of text fields give us information about the cursor and selection in the text. When nothing is selected, these two properties hold the same number, indicating the position of the cursor. For example, 0 indicates the start of the text, and 10 indicates the cursor is after the 10th character. When part of the field is selected, the two properties will differ, giving us the start and end of the selected text. Like value
, these properties may also be written to.
As an example, imagine you are writing an article about Khasekhemwy but have some trouble spelling his name. The following code wires up a <textarea>
tag with an event handler that, when you press F2, inserts the string “Khasekhemwy” for you.
<textarea></textarea> <script> var textarea = document.querySelector("textarea"); textarea.addEventListener("keydown", function(event) { // The key code for F2 happens to be 113 if (event.keyCode == 113) { replaceSelection(textarea, "Khasekhemwy"); event.preventDefault(); } }); function replaceSelection(field, word) { var from = field.selectionStart, to = field.selectionEnd; field.value = field.value.slice(0, from) + word + field.value.slice(to); // Put the cursor after the word field.selectionStart = field.selectionEnd = from + word.length; } </script>
The replaceSelection
function replaces the currently selected part of a text field’s content with the given word and then moves the cursor after that word so that the user can continue typing.
The "change"
event for a text field does not fire every time something is typed. Rather, it fires when the field loses focus after its content was changed. To respond immediately to changes in a text field, you should register a handler for the "input"
event instead, which fires for every time the user types a character, deletes text, or otherwise manipulates the field’s content.
The following example shows a text field and a counter showing the current length of the text entered
<input type="text"> length: <span id="length">0</span> <script> var text = document.querySelector("input"); var output = document.querySelector("#length"); text.addEventListener("input", function() { output.textContent = text.value.length; }); </script>
Checkboxes and radio buttons
A checkbox field is a simple binary toggle. Its value can be extracted or changed through its checked
property, which holds a Boolean value.
<input type="checkbox" id="purple"> <label for="purple">Make this page purple</label> <script> var checkbox = document.querySelector("#purple"); checkbox.addEventListener("change", function() { document.body.style.background = checkbox.checked ? "mediumpurple" : ""; }); </script>
The <label>
tag is used to associate a piece of text with an input field. Its for
attribute should refer to the id
of the field. Clicking the label will activate the field, which focuses it and toggles its value when it is a checkbox or radio button.
A radio button is similar to a checkbox, but it’s implicitly linked to other radio buttons with the same name
attribute so that only one of them can be active at any time.
Color: <input type="radio" name="color" value="mediumpurple"> Purple <input type="radio" name="color" value="lightgreen"> Green <input type="radio" name="color" value="lightblue"> Blue <script> var buttons = document.getElementsByName("color"); function setColor(event) { document.body.style.background = event.target.value; } for (var i = 0; i < buttons.length; i++) buttons[i].addEventListener("change", setColor); </script>
document.getElementsByName
メソッドは、指定されたname
属性を持つすべての要素を取得します。この例では、それらの要素をループ処理して(返されたコレクションは本当の配列ではないため、forEach
ではなく通常のfor
ループを使用)、各要素にイベントハンドラを登録しています。イベントオブジェクトには、イベントをトリガーした要素を参照するtarget
プロパティがあることを覚えておいてください。これは、異なる要素で呼び出され、現在のターゲットにアクセスする必要があるこのようなイベントハンドラで役立つことがよくあります。
セレクトフィールド
セレクトフィールドは概念的にはラジオボタンに似ています。どちらも、ユーザーがオプションのセットから選択することを可能にします。しかし、ラジオボタンはオプションのレイアウトを制御下に置くのに対し、<select>
タグの外観はブラウザによって決定されます。
セレクトフィールドには、ラジオボックスではなくチェックボックスのリストに似たバリエーションもあります。multiple
属性が指定されると、<select>
タグはユーザーが単一のオプションではなく、任意の数のオプションを選択することを可能にします。
<select multiple> <option>Pancakes</option> <option>Pudding</option> <option>Ice cream</option> </select>
これは、ほとんどのブラウザで、通常はドロップダウンコントロールとして描画され、開いたときにのみオプションが表示されるmultiple
ではないセレクトフィールドとは異なる表示になります。
<select>
タグへのsize
属性は、同時に表示されるオプションの数を設定するために使用され、ドロップダウンの外観を明示的に制御できます。たとえば、size
属性を"3"
に設定すると、multiple
オプションが有効になっているかどうかに関係なく、フィールドに3行が表示されます。
各<option>
タグには値があります。この値はvalue
属性で定義できますが、それが指定されていない場合、オプション内のテキストがオプションの値としてカウントされます。<select>
要素のvalue
プロパティは、現在選択されているオプションを反映します。ただし、multiple
フィールドの場合、このプロパティは、現在選択されているオプションの1つだけの値を与えるため、あまり意味がありません。
<select>
フィールドの<option>
タグは、フィールドのoptions
プロパティを通じて配列のようなオブジェクトとしてアクセスできます。各オプションにはselected
というプロパティがあり、そのオプションが現在選択されているかどうかを示します。このプロパティは、オプションの選択または選択解除にも書き込むことができます。
次の例では、multiple
セレクトフィールドから選択された値を抽出し、それらを使用して個々のビットから2進数を構成します。複数のオプションを選択するには、Ctrlキー(Macの場合はCommandキー)を押したままにします。
<select multiple> <option value="1">0001</option> <option value="2">0010</option> <option value="4">0100</option> <option value="8">1000</option> </select> = <span id="output">0</span> <script> var select = document.querySelector("select"); var output = document.querySelector("#output"); select.addEventListener("change", function() { var number = 0; for (var i = 0; i < select.options.length; i++) { var option = select.options[i]; if (option.selected) number += Number(option.value); } output.textContent = number; }); </script>
ファイルフィールド
ファイルフィールドは、もともとフォームを通じてブラウザのマシンからファイルをアップロードする方法として設計されました。最新のブラウザでは、JavaScriptプログラムからそのようなファイルを読み取る方法も提供します。フィールドはゲートキーパーのような役割を果たします。スクリプトはユーザーのコンピューターからプライベートファイルを単純に読み始めることはできませんが、ユーザーがそのようなフィールドでファイルを選択した場合、ブラウザはそのアクションをスクリプトがファイルを読み取ってもよいことを意味すると解釈します。
ファイルフィールドは通常、「ファイルを選択」や「参照」のようなラベルの付いたボタンのように見え、選択したファイルに関する情報がその横に表示されます。
<input type="file"> <script> var input = document.querySelector("input"); input.addEventListener("change", function() { if (input.files.length > 0) { var file = input.files[0]; console.log("You chose", file.name); if (file.type) console.log("It has type", file.type); } }); </script>
ファイルフィールド要素のfiles
プロパティは、フィールドで選択されたファイルを格納する配列のようなオブジェクト(これも本当の配列ではない)です。最初は空です。単なるfile
プロパティがない理由は、ファイルフィールドもmultiple
属性をサポートしており、同時に複数のファイルを選択できるためです。
files
プロパティのオブジェクトには、name
(ファイル名)、size
(ファイルサイズ(バイト単位))、type
(ファイルのメディアタイプ(text/plain
やimage/jpeg
など))などのプロパティがあります。
ファイルの内容を含むプロパティはありません。それらを取得するには、もう少し複雑な手順が必要です。ディスクからのファイルの読み取りには時間がかかる可能性があるため、ドキュメントのフリーズを防ぐには、インターフェースを非同期にする必要があります。FileReader
コンストラクタは、ファイル用のXMLHttpRequest
に似ていると考えてください。
<input type="file" multiple> <script> var input = document.querySelector("input"); input.addEventListener("change", function() { Array.prototype.forEach.call(input.files, function(file) { var reader = new FileReader(); reader.addEventListener("load", function() { console.log("File", file.name, "starts with", reader.result.slice(0, 20)); }); reader.readAsText(file); }); }); </script>
ファイルの読み取りは、FileReader
オブジェクトを作成し、その"load"
イベントハンドラを登録し、readAsText
メソッドを呼び出して読み取るファイルを指定することで行われます。読み込みが完了すると、リーダーのresult
プロパティにファイルの内容が含まれます。
この例では、通常のループではイベントハンドラから正しいfile
オブジェクトとreader
オブジェクトを取得するのが面倒なため、Array.prototype.forEach
を使用して配列を反復処理しています。変数はループのすべての反復で共有されます。
ファイルの読み取りに失敗した場合、FileReader
は"error"
イベントも発生させます。エラーオブジェクト自体は、リーダーのerror
プロパティに格納されます。別の矛盾した非同期インターフェースの詳細を覚える必要がない場合は、Promise
(第17章参照)でラップすることができます。
function readFile(file) { return new Promise(function(succeed, fail) { var reader = new FileReader(); reader.addEventListener("load", function() { succeed(reader.result); }); reader.addEventListener("error", function() { fail(reader.error); }); reader.readAsText(file); }); }
ファイルのスライスを取得し、その結果(いわゆるblobオブジェクト)をファイルリーダーに渡すことで、ファイルの一部のみを読み取ることができます。
クライアントサイドでのデータの保存
少々のJavaScriptを使ったシンプルなHTMLページは、「ミニアプリケーション」—日常の作業を自動化する小さなヘルパープログラム—にとって優れた媒体となる可能性があります。いくつかのフォームフィールドとイベントハンドラを接続することで、摂氏と華氏の変換から、マスターパスワードとウェブサイト名からパスワードを計算することまで、何でも行うことができます。
このようなアプリケーションがセッション間で何かを覚えている必要がある場合、ページが閉じられるたびに破棄されるため、JavaScript変数を使用することはできません。サーバーをセットアップし、インターネットに接続し、アプリケーションでそこに何かを保存することができます。第20章でその方法を説明します。しかし、これは多くの余分な作業と複雑さを加えます。データをブラウザに保存するだけで十分な場合があります。しかし、どのようにすればよいのでしょうか?
localStorage
オブジェクトに文字列データを入れることで、ページのリロードをしても残るように保存できます。このオブジェクトを使用すると、この例のように、名前(これも文字列)の下に文字列値を保存できます。
localStorage.setItem("username", "marijn"); console.log(localStorage.getItem("username")); // → marijn localStorage.removeItem("username");
localStorage
の値は、上書きされるまで、removeItem
で削除されるまで、またはユーザーがローカルデータをクリアするまで保持されます。
異なるドメインのサイトは、異なるストレージコンパートメントを取得します。つまり、特定のウェブサイトによってlocalStorage
に保存されたデータは、原則として、同じサイトのスクリプトによってのみ読み取り(および上書き)できます。
ブラウザは、サイトがlocalStorage
に保存できるデータのサイズにも制限を設けており、通常は数メガバイトのオーダーです。この制限と、人々のハードディスクをジャンクでいっぱいにすることは実際には利益にならないという事実により、この機能が過剰なスペースを消費することが防止されます。
次のコードは、シンプルなメモ帳アプリケーションを実装しています。ユーザーのメモをオブジェクトとして保持し、メモのタイトルをコンテンツ文字列に関連付けます。このオブジェクトはJSONとしてエンコードされ、localStorage
に保存されます。ユーザーは<select>
フィールドからメモを選択し、<textarea>
でそのメモのテキストを変更できます。ボタンをクリックすることでメモを追加できます。
Notes: <select id="list"></select> <button onclick="addNote()">new</button><br> <textarea id="currentnote" style="width: 100%; height: 10em"> </textarea> <script> var list = document.querySelector("#list"); function addToList(name) { var option = document.createElement("option"); option.textContent = name; list.appendChild(option); } // Initialize the list from localStorage var notes = JSON.parse(localStorage.getItem("notes")) || {"shopping list": ""}; for (var name in notes) if (notes.hasOwnProperty(name)) addToList(name); function saveToStorage() { localStorage.setItem("notes", JSON.stringify(notes)); } var current = document.querySelector("#currentnote"); current.value = notes[list.value]; list.addEventListener("change", function() { current.value = notes[list.value]; }); current.addEventListener("change", function() { notes[list.value] = current.value; saveToStorage(); }); function addNote() { var name = prompt("Note name", ""); if (!name) return; if (!notes.hasOwnProperty(name)) { notes[name] = ""; addToList(name); saveToStorage(); } list.value = name; current.value = notes[name]; } </script>
スクリプトは、localStorage
に保存されている値に、またはそれが存在しない場合は、空の"shopping list"
メモのみを持つシンプルなオブジェクトにnotes
変数を初期化します。localStorage
から存在しないフィールドを読み取るとnull
が返されます。null
をJSON.parse
に渡すと、文字列"null"
が解析され、null
が返されます。したがって、このような状況では||
演算子を使用してデフォルト値を提供できます。
メモデータが変更されるたびに(新しいメモが追加された場合や既存のメモが変更された場合)、saveToStorage
関数が呼び出され、ストレージフィールドが更新されます。このアプリケーションが少数のメモではなく数千のメモを処理することを目的としていた場合、これはコストが高すぎ、各メモに独自のストレージフィールドを与えるなど、より複雑な保存方法を考案する必要があります。
ユーザーが新しいメモを追加する場合、<select>
フィールドには同じことを行う"change"
ハンドラがありますが、テキストフィールドを明示的に更新する必要があります。これは、"change"
イベントがフィールドの値をユーザーが変更した場合にのみ発生し、スクリプトが変更した場合には発生しないためです。
localStorage
と同様のオブジェクトとしてsessionStorage
があります。両者の違いは、sessionStorage
の内容は各セッションの最後に忘れられることです。ほとんどのブラウザでは、ブラウザが閉じられるたびにという意味になります。
まとめ
HTMLは、テキストフィールド、チェックボックス、複数選択フィールド、ファイルピッカーなど、さまざまな種類のフォームフィールドを表すことができます。
このようなフィールドは、JavaScriptで検査および操作できます。変更されると"change"
イベントが発生し、テキストが入力されると"input"
イベントが発生し、さまざまなキーボードイベントが発生します。これらのイベントにより、ユーザーがフィールドを操作していることを認識できます。value
(テキストフィールドとセレクトフィールドの場合)やchecked
(チェックボックスとラジオボタンの場合)などのプロパティを使用して、フィールドの内容を読み取ったり設定したりします。
フォームが送信されると、その"submit"
イベントが発生します。JavaScriptハンドラは、そのイベントに対してpreventDefault
を呼び出して、送信が行われないようにすることができます。フォームフィールド要素は<form>
タグでラップする必要はありません。
ユーザーがファイルピッカーフィールドでローカルファイルシステムからファイルを選択した場合、FileReader
インターフェースを使用して、JavaScriptプログラムからこのファイルの内容にアクセスできます。
localStorage
オブジェクトとsessionStorage
オブジェクトを使用して、ページのリロードをしても残るように情報を保存できます。前者はデータを永久に(またはユーザーがクリアするまで)、後者はブラウザが閉じられるまで保存します。
練習問題
JavaScript ワークベンチ
JavaScriptコードを入力して実行できるインターフェースを作成します。
<textarea>
フィールドの横にボタンを配置します。ボタンを押すと、第10章で見た`Function`コンストラクタを使用してテキストを関数にラップし、それを呼び出します。関数の戻り値、または発生したエラーを文字列に変換し、テキストフィールドの後に表示します。
<textarea id="code">return "hi";</textarea> <button id="button">Run</button> <pre id="output"></pre> <script> // Your code here. </script>
HTMLで定義された要素にアクセスするには、`document.querySelector`または`document.getElementById`を使用します。ボタンの` "click"`または` "mousedown"`イベントのイベントハンドラーは、テキストフィールドの`value`プロパティを取得し、それに`new Function`を呼び出すことができます。
`new Function`の呼び出しとその結果の呼び出しの両方を`try`ブロックでラップして、発生する例外をキャッチできるようにします。この場合、どのような種類の例外を探しているのかは実際にはわからないため、すべてをキャッチします。
出力要素の`textContent`プロパティを使用して、文字列メッセージで埋めることができます。または、古いコンテンツを残しておきたい場合は、`document.createTextNode`を使用して新しいテキストノードを作成し、要素に追加します。すべての出力が1行に表示されないように、最後に改行文字を追加することを忘れないでください。
自動補完
ユーザーが入力すると、フィールドの下に候補値のリストが表示されるようにテキストフィールドを拡張します。使用可能な候補値の配列があり、入力されたテキストで始まる候補値を表示する必要があります。候補がクリックされると、テキストフィールドの現在の値がそれに置き換えられます。
<input type="text" id="field"> <div id="suggestions" style="cursor: pointer"></div> <script> // Builds up an array with global variable names, like // 'alert', 'document', and 'scrollTo' var terms = []; for (var name in window) terms.push(name); // Your code here. </script>
候補リストを更新するのに最適なイベントは` "input"`です。これは、フィールドの内容が変更されるとすぐに発生します。
次に、用語の配列をループ処理し、指定された文字列で始まるかどうかを確認します。たとえば、`indexOf`を呼び出して、結果が0かどうかを確認できます。一致する文字列ごとに、候補`<div>`に要素を追加します。`textContent`を空の文字列に設定するなど、候補の更新を開始するたびに空にする必要があるでしょう。
各候補要素に` "click"`イベントハンドラーを追加するか、それらを保持する外部`<div>`に1つのイベントハンドラーを追加し、イベントの`target`プロパティを確認して、どの候補がクリックされたかを確認できます。
DOMノードから候補テキストを取得するには、`textContent`を確認するか、要素を作成するときにテキストを明示的に格納する属性を設定できます。
ライフゲーム
ライフゲームは、グリッド上に人工的な「生命」を作成する単純なシミュレーションで、各セルは生きているかいないかのいずれかです。各世代(ターン)で、次のルールが適用されます。
隣接セルとは、斜めに隣接するセルを含む、隣接するすべてのセルを指します。
これらのルールは、一度にグリッド全体に適用され、1つのセルずつではありません。つまり、隣接セルのカウントは世代の開始時の状況に基づいており、この世代中に隣接セルで発生する変化は、特定のセルの新しい状態に影響を与えません。
適切なデータ構造を使用してこのゲームを実装します。`Math.random`を使用して、最初はランダムなパターンでグリッドを埋めます。チェックボックスのグリッドとして表示し、その横に次の世代に進むためのボタンを配置します。ユーザーがチェックボックスをチェックまたはオフにすると、その変更は次の世代の計算に含まれます。
<div id="grid"></div> <button id="next">Next generation</button> <script> // Your code here. </script>
変更が概念的に同時に発生するという問題を解決するには、世代の計算を、1つのグリッドを受け取り、次のターンを表す新しいグリッドを生成する純粋関数として考えてみてください。
グリッドの表現は、第7章と第15章で示されている方法のいずれかで行うことができます。生きている隣接セルのカウントは、隣接する座標をループ処理する2つのネストされたループで行うことができます。フィールド外のセルをカウントしないように注意し、隣接セルのカウントを行う中央のセルは無視してください。
チェックボックスの変更を次の世代に反映させるには、2つの方法があります。イベントハンドラーがこれらの変更を認識して現在のグリッドを更新するか、次のターンの計算の前にチェックボックスの値から新しいグリッドを生成します。
イベントハンドラーを使用する場合は、各チェックボックスに対応する位置を識別する属性を追加して、変更するセルを簡単に特定できるようにすることができます。
チェックボックスのグリッドを描画するには、`<table>`要素を使用するか(第13章参照)、すべてを同じ要素に配置し、行間に`<br>`(改行)要素を配置します。