<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>yurubo blog</title><description>技術と日常のメモ</description><link>https://blog.yurubo.com/</link><language>ja</language><item><title>Chrome DevToolsで学ぶWebページの調査実践</title><link>https://blog.yurubo.com/chrome-devtools-web-investigation-practical-guide/</link><guid isPermaLink="true">https://blog.yurubo.com/chrome-devtools-web-investigation-practical-guide/</guid><description>HTML・CSS・通信・フォーム・キーボード操作を、ローカル教材と12枚の実画面で調べる約5万字の実習記事。観測と推測を分け、再現できる調査報告までまとめます。</description><pubDate>Sat, 19 Sep 2026 06:33:55 GMT</pubDate><content:encoded>&lt;p&gt;画面の違いを記録し、HTML、CSS、通信、操作のどこを調べるかを判断するための実習記事です。
掲載画像は、この原稿用に作成したローカル教材をmacOSのChromeで操作して撮影しました。
DevTools本体の撮影ではありません。DevToolsの操作は公式資料に基づく追試手順として説明します。
&lt;a href=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/95e0124a2192efbe/devtools-lesson.zip&quot; rel=&quot;noopener noreferrer&quot;&gt;実習教材をダウンロード（ZIP）&lt;/a&gt;し、解凍したフォルダーのREADMEに従って起動してください。&lt;/p&gt;
&lt;h2&gt;1. 調査を始める前に症状を記録する&lt;/h2&gt;
&lt;p&gt;Webブラウザー上で表示の崩れや意図しない挙動に直面したとき、あて推量でスタイルを書き換えると原因の特定が難しくなります。最初に、操作の手順と画面の変化をメモします。&lt;/p&gt;
&lt;h3&gt;観測と推測を分ける四段階の作業表&lt;/h3&gt;
&lt;p&gt;調査手順を&lt;strong&gt;観測&lt;/strong&gt;、&lt;strong&gt;仮説&lt;/strong&gt;、&lt;strong&gt;変更&lt;/strong&gt;、&lt;strong&gt;再確認&lt;/strong&gt;という四つの段階に整理した作業表を用います。作業表の各項目には、調査者が行う判断の役割を明確に割り当てます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;観測&lt;/strong&gt;：操作を行った環境、実行した手順、画面に現れた具体的な文字列や寸法の変化など、客観的な事実のみを書き出します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;仮説&lt;/strong&gt;：観測された事実の差分を生み出していると考えられる構造的な理由を、一つだけ推定します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;変更&lt;/strong&gt;：仮説の妥当性を確かめるために、最小単位のスタイル指定や属性値の書き換えを一度に一箇所だけ行います。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;再確認&lt;/strong&gt;：変更を加えたあとの画面を再度観測し、仮説どおりの結果が得られたか、あるいは新たな差分が生じたかを記録します。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;各段階を一行ずつ記録に残しながら進めることで、手詰まりになった場合でも直前の確定情報まで安全に作業を巻き戻せます。&lt;/p&gt;
&lt;h3&gt;調査用ローカル教材の構成と表示の確認&lt;/h3&gt;
&lt;p&gt;実習では、手元の端末でHTTPサーバーを立ち上げ、HTMLファイルとJSONファイルの二つを配信して動作させるローカル教材を使用します。教材の起動手順や前提環境の配置については、配布物に含まれる &lt;code&gt;README&lt;/code&gt; の案内に従って準備を済ませてください。&lt;/p&gt;
&lt;p&gt;ページ下部のフッターにも明記されているとおり、この教材に対してブラウザー上で行ったDOMやスタイルの書き換えは、閲覧中の一時的なメモリ上にのみ保持されます。ブラウザーの再読み込みを行えば、スクリプトが読み込まれた直後の初期状態に復帰します。外部のサーバーへフォーム情報や閲覧ログを送信する処理も含まれていません。&lt;/p&gt;
&lt;p&gt;教材のページを開くと、URLの末尾に付与されたクエリパラメータ &lt;code&gt;?case=overview&lt;/code&gt; に応じて初期画面が描画されます。macOS上のChromeでこのローカル教材を開いた際の初期表示が、次の画像です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/ea1641877f8d5c8c/images/01-overview.png&quot; alt=&quot;教材の初期画面。表示、通信、操作の三つの観察領域&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;画面上には「見え方と原因を分けて記録する」という表題とともに、表示、通信、操作という三つの観察領域を示したカードが横並びに配置されています。この画像自体はChromeのDevToolsそのものを撮影したものではなく、調査の対象となる教材ページ自体の初期状態を示した記録です。調査を始める際には、まずこのように「操作を何も加えていない基準の画面」を控え、何が書かれており、どの枠線がどこに配置されているかを把握します。&lt;/p&gt;
&lt;h3&gt;Chrome DevToolsの起動と配置の違い&lt;/h3&gt;
&lt;p&gt;Webページの内部状態を観察するための標準的な機能が、Chromeに統合されているDevToolsです。公式資料の &lt;a href=&quot;https://developer.chrome.com/docs/devtools/open&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevTools の概要と開き方&lt;/a&gt; にあるように、DevToolsはページの要素を右クリックして検証を選ぶか、ブラウザー右上のメニューから起動できます。ウィンドウの配置は利用環境に合わせて変更可能です。&lt;/p&gt;
&lt;p&gt;DevToolsは手元の画面で観測した差異の理由を調べるための補助具であり、最初に見るべき対象は常にWebブラウザーがレンダリングしている教材ページそのものです。&lt;/p&gt;
&lt;h3&gt;調査メモの作成例と記録の粒度&lt;/h3&gt;
&lt;p&gt;客観的な事実に基づいた調査メモを作成する習慣をつけます。調査メモに書くべき内容は主観的な印象ではなく、第三者が同じ手順を踏んだときに同じ挙動を確認できる客観的な記述です。教材の初期表示を確認した段階では、次のような形式で記録を残します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[調査メモ: 初回観測]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;対象URL: http://127.0.0.1:9033/demo/?case=overview&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;観測環境: macOS, Google Chrome&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;初期状態の表示:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- ヘッダーに「Web調査の練習室」の文字列が存在する。&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 画面中央に「01 / 調査の入口」という小さなタグと「見え方と原因を分けて記録する」という大見出しが表示されている。&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 大見出しの下に「表示」「通信」「操作」の3枚のカードが横並びで配置されている。&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- カードの下部には「Elements → Styles」「Network」「Accessibility」という対応パネルの文字列がコード形式で確認できる。&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- ページ最下部に「変更はこの教材の表示に限ります。再読み込みで初期状態に戻ります。」のフッター文がある。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このメモには個人の解釈や推測による原因論を交えず、「3枚のカードが横並びで配置されている」という具体的な配置の事実だけを記載しています。記録の粒度を視覚情報とテキスト情報に限定しておくことで、後続のステップで変更を加えた際、どの部分が意図どおりに変化したかを明確に対比できます。&lt;/p&gt;
&lt;h3&gt;症状の切り分けを行うための着眼点&lt;/h3&gt;
&lt;p&gt;問題の所在を突き止めるには、事象を適切なカテゴリに腑分けして考える必要があります。教材の「01 / 調査の入口」の画面で提示されているとおり、調査の着眼点は大きく三つに分類できます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表示の切り分け&lt;/strong&gt;：要素の寸法、余白の広さ、テキストの折り返しなど、純粋な視覚的配置に関する問題です。CSSのプロパティ値やボックスモデルの計算規則が関与している可能性を検討します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通信の切り分け&lt;/strong&gt;：外部から取得すべきデータが届いていない、または通信自体が失敗しているといった入出力の問題です。リクエストされたURLの正しさやHTTPステータスコードを調べる対象とします。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作の切り分け&lt;/strong&gt;：クリック、文字入力、Tabキーによる移動など、利用者のアクションに対するインターフェースの反応に関する問題です。イベントの発火やDOM要素の属性値の変化を確かめます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例えば画面上の表示枠から文字がはみ出している場合、親要素のCSSに過剰な固定幅が指定されている可能性もあれば、通信で想定よりも長い文面が送られてきた可能性、あるいは操作によって動的にスタイルクラスが変更された可能性も考えられます。観察された一つの症状に対して、三つの領域のどこに構造的な理由があるのかを仮説として立て、一つずつ検証を重ねていく姿勢が求められます。&lt;/p&gt;
&lt;h2&gt;2. 初期HTMLと操作後のDOMを読み分ける&lt;/h2&gt;
&lt;p&gt;Webブラウザーは受信したHTMLを構文解析してメモリ上にDOM（Document Object Model）を構築し、その後のJavaScriptの実行や利用者の操作によってその木構造を書き換えます。
画面に見えている要素の正体を突き止めるには、送信されてきた静的なファイルの内容と、ブラウザー内部で変化した現在の構造を明確に分けて観察しなければなりません。&lt;/p&gt;
&lt;h3&gt;画面に現れる文字と初期コードの落とし穴&lt;/h3&gt;
&lt;p&gt;Webページ上の表示崩れや文言の不整合を調査する際、スクリプトによって作られる要素は、HTMLのマークアップとしては存在しないことがあります。文字列や生成処理はソースコードの検索でも探せますが、検索結果だけでは現在の表示状態を確定できません。
ソースコードそのものを探す作業と、動的に生成された現在の状態を調べる作業を切り離す必要があります。&lt;/p&gt;
&lt;p&gt;本節で対象とする画面は、HTTPサーバーで配信されるHTMLファイルとJSONデータから構成される検証用教材の一部です。
ブラウザーで &lt;code&gt;http://127.0.0.1:9033/demo/?case=dom&lt;/code&gt; にアクセスすると、以下のHTML文字列が読み込まれて領域内に展開されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;tag&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;04 / 状態とDOM&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;操作のあとで増える要素&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;「詳細を表示」で説明要素を追加し、「詳細を閉じる」で取り除きます。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;toggle&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-expanded&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-controls&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;detail&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;詳細を表示&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;detail&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;note&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;HTMLファイルに書かれた初期状態と、操作後のDOMを区別して観察します。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;初期状態のコードでは &lt;code&gt;&amp;lt;div id=&quot;detail&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; の中身は空であり、子要素は含まれていません。
この状態でブラウザーに表示される外観が次の画像です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/37265273d3b50a7c/images/06-dom-before.png&quot; alt=&quot;詳細を表示する前。説明段落はまだない&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;画面上には「詳細を表示」というラベルのボタンが配置されているだけで、説明文の領域は見当たりません。
この外観は初期HTMLの構造と一致しています。&lt;/p&gt;
&lt;h3&gt;操作によって生じるDOMツリーの書き換え&lt;/h3&gt;
&lt;p&gt;教材に含まれるスクリプトでは、ボタンのクリックイベントに伴って次の処理が実行されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// demo/index.html の抜粋&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (mode &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;dom&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#toggle&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onclick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; button&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; event.currentTarget;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; button.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getAttribute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;aria-expanded&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;true&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    button.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setAttribute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;aria-expanded&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;open));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    button.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; open &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;詳細を表示&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;詳細を閉じる&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; detail&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#detail&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    detail.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;replaceChildren&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;open) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;p&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      p.className &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;result&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      p.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;この説明はボタン操作後にJavaScriptで追加されました。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      detail.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;append&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(p);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;利用者がボタンを押すと、&lt;code&gt;aria-expanded&lt;/code&gt; 属性が反転し、表示テキストが「詳細を閉じる」に更新されます。
続いて &lt;code&gt;detail.replaceChildren()&lt;/code&gt; で &lt;code&gt;#detail&lt;/code&gt; の中身が一度空にリセットされた後、開く操作（&lt;code&gt;!open&lt;/code&gt;）であれば新しい &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; 要素が生成されて挿入されます。
macOSのGoogle Chrome上でボタンをクリックしたあとの表示が次の画像です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/9a5547d57b580003/images/07-dom-after.png&quot; alt=&quot;詳細を表示した後。説明段落と閉じるボタン&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;枠線付きの領域が出現し、「この説明はボタン操作後にJavaScriptで追加されました。」という段落が表示されます。
ボタンの表記も「詳細を閉じる」に変化します。
この段落の文字列はHTMLファイル内のJavaScriptにはありますが、初期の説明用コンテナには段落要素がありません。ブラウザーが保持するDOMを観察対象とすることで、スクリプトが生成した要素がツリーへ組み込まれた事実を把握できます。&lt;/p&gt;
&lt;h3&gt;Elementsパネルで現在の状態を直接確かめる&lt;/h3&gt;
&lt;p&gt;動的に変化する構造を調べるには開発者向けツールを活用します。
&lt;a href=&quot;https://developer.chrome.com/docs/devtools/dom&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevToolsドキュメント&lt;/a&gt;にあるとおり、Elementsパネルでは現在のDOMツリーの要素を直接検査できます。
ページのソース表示が生データを表示するのに対し、ElementsパネルはJavaScriptの変更が適用された現在のDOMを表示します。&lt;/p&gt;
&lt;p&gt;教材ソースと操作後の画面から整理した変更点は次のとおりです。Elementsパネル自体は撮影していません。追試ではこれらの項目を照合してください。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;観測対象&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;操作前（初期状態）&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;操作後（ボタンクリック後）&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;変化の意味と役割&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;button#toggle&lt;/code&gt; の &lt;code&gt;textContent&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;詳細を表示&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;詳細を閉じる&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;次に行える操作を示す可視ラベル&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;button#toggle&lt;/code&gt; の &lt;code&gt;aria-expanded&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;false&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;true&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;制御対象が展開されているかを示す支援技術向け属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;div#detail&lt;/code&gt; の子要素数&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;0個（空要素）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1個（&lt;code&gt;&amp;lt;p class=&quot;result&quot;&amp;gt;&lt;/code&gt;）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;説明コンテンツのノード追加&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&amp;lt;p class=&quot;result&quot;&amp;gt;&lt;/code&gt; のテキスト&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;なし（要素自体が不在）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;この説明はボタン操作後に...&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;スクリプトによって生成された文字列&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;外観上の段落追加だけでなく、&lt;code&gt;aria-expanded&lt;/code&gt; 属性値の遷移も確認できます。&lt;/p&gt;
&lt;h3&gt;閉じる操作と反復追試で完全な往復を確認する&lt;/h3&gt;
&lt;p&gt;状態を切り替えるUIでは、閉じた状態に戻したときにDOMが復元されるかの追試も必要です。
今回の教材コードでは、開いた状態でもう一度ボタンを押すと &lt;code&gt;open&lt;/code&gt; が真となり、ボタンの属性は &lt;code&gt;&quot;false&quot;&lt;/code&gt; に、テキストは「詳細を表示」に戻ります。
コンテナ要素に対して引数なしで &lt;code&gt;detail.replaceChildren()&lt;/code&gt; が呼び出されると、配下のすべての子ノードが消去されます。
&lt;code&gt;if (!open)&lt;/code&gt; ブロックは実行されないため、&lt;code&gt;#detail&lt;/code&gt; 配下は初期状態と同じ空の状態へ戻ります。&lt;/p&gt;
&lt;p&gt;今回の教材では、ボタン操作によって次の3点を確認します。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ボタンの表記が再び「詳細を表示」に戻っているか&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-expanded&lt;/code&gt; 属性が &lt;code&gt;&quot;false&quot;&lt;/code&gt; に戻っているか&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#detail&lt;/code&gt; の中から &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; 要素が消去され、初期状態の空要素に戻っているか&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;仮に &lt;code&gt;p.style.display = &apos;none&apos;&lt;/code&gt; のようなスタイル指定で非表示にされた場合はDOM上に要素が残存しますが、本教材のように &lt;code&gt;replaceChildren()&lt;/code&gt; でノードを破棄した場合はDOMツリーから要素が消失します。
ノードが消去されたのか、スタイルで非表示になっただけなのかを峻別することが重要です。&lt;/p&gt;
&lt;h2&gt;3. CSSの指定と使われた値を切り分ける&lt;/h2&gt;
&lt;p&gt;ブラウザーの表示が崩れたとき、スタイルシートに書いたプロパティを眺めるだけでは原因を特定できない場面があります。ソースコード上の記述はブラウザーに対する指定の候補であり、規則の衝突や継承、インライン指定などを経て最終的な描画値が決まります。&lt;/p&gt;
&lt;p&gt;調査では、書かれたCSS宣言とブラウザーが適用している値を分けて観察します。&lt;a href=&quot;https://drafts.csswg.org/css-cascade-5/#value-stages&quot; rel=&quot;noopener noreferrer&quot;&gt;CSS Cascading仕様&lt;/a&gt;には指定値、計算値、使用値という別々の段階があるため、DevToolsの表示をすべて同じ意味の値とみなさないようにします。Chrome DevToolsのElementsパネルには、この二つの段階を個別に点検するためのタブが用意されています。&lt;/p&gt;
&lt;h3&gt;StylesタブとComputedタブの役割を分担する&lt;/h3&gt;
&lt;p&gt;Elementsパネルの補助ペインに並ぶStylesタブとComputedタブは、CSSが要素に作用する工程の異なる側面を写し出します。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/css/reference&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevToolsの公式リファレンス（CSS features reference）&lt;/a&gt;では、StylesペインでCSS規則の宣言を調べ、Computedペインで最終的に計算・適用された値を調べられると案内されています。Stylesペインに表示される宣言の取り消し線を手掛かりにすることで、どの規則が上書きされたかを追跡できます。&lt;/p&gt;
&lt;p&gt;Stylesタブは、選択中の要素に一致したセレクターと宣言のリストを表示します。各宣言の出自となったスタイルシートのファイル名や行番号、規則同士の優先順位関係が確認できます。取り消し線などの表示は上書きや無効な宣言を探す手掛かりになりますが、表示理由とプロパティの状態も確認してください。&lt;/p&gt;
&lt;p&gt;これに対してComputedタブは、適用された値を確認するために使います。すべてのプロパティが同じ段階でpxに変換されるわけではありません。DOM要素が保持しているプロパティ値そのものを確認したいときは、Computedタブを参照します。&lt;/p&gt;
&lt;h3&gt;調査対象のHTML構造と初期のスタイル定義&lt;/h3&gt;
&lt;p&gt;本章で用いる教材ページ（&lt;code&gt;http://127.0.0.1:9033/demo/&lt;/code&gt;）には、スタイルの適用関係を切り分ける構造があらかじめ組み込まれています。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;box-sizing&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border-box&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#edf1f2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#182b34&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;font&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;17&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.75&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; system-ui&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* ...略... */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.box&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;320&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; solid&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; #177887&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#dceff2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;tag&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;02 / 幅の内訳&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;同じ width でも外側の幅が変わる&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;指定は &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;width: 320px; padding: 24px; border: 4px solid&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt; です。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;contentBox&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;content-box にする&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;borderBox&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;secondary&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;border-box にする&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box-sizing:content-box&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;カードの内容領域&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;measure&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;note&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;枠線を含む幅を測っています。margin はこの幅に含まれません。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;教材の先頭では、全称セレクター &lt;code&gt;*&lt;/code&gt; に対して &lt;code&gt;box-sizing: border-box&lt;/code&gt; が宣言されています。しかし、&lt;code&gt;&amp;lt;div id=&quot;box&quot; class=&quot;box&quot;&amp;gt;&lt;/code&gt; 要素にはインラインスタイルとして &lt;code&gt;style=&quot;box-sizing:content-box&quot;&lt;/code&gt; が直接指定されています。さらにクラス名 &lt;code&gt;.box&lt;/code&gt; には &lt;code&gt;width: 320px&lt;/code&gt;、&lt;code&gt;padding: 24px&lt;/code&gt;、&lt;code&gt;border: 4px solid #177887&lt;/code&gt; が定義されています。&lt;/p&gt;
&lt;h3&gt;全称セレクターとインラインスタイルの競合を追う&lt;/h3&gt;
&lt;p&gt;以下は公式資料と教材ソースに基づく追試手順です。今回はDevToolsのパネル表示そのものを確認していません。&lt;/p&gt;
&lt;p&gt;ElementsパネルのDOMツリーで &lt;code&gt;&amp;lt;div id=&quot;box&quot;&amp;gt;&lt;/code&gt; を選択してStylesタブを開くと、最上段に要素自身のインラインスタイルを示す &lt;code&gt;element.style&lt;/code&gt; が表示されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;element.style {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  box-sizing: content-box;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;その下方にスクロールすると、クラスセレクター &lt;code&gt;.box&lt;/code&gt;、さらに下方に全称セレクター &lt;code&gt;*&lt;/code&gt; のブロックが現れます。全称セレクターの枠内にある &lt;code&gt;box-sizing: border-box&lt;/code&gt; には取り消し線が引かれます。&lt;/p&gt;
&lt;p&gt;CSSのカスケードと詳細度の規則により、要素タグの &lt;code&gt;style&lt;/code&gt; 属性に書かれたインライン宣言が、この教材の通常のクラス指定と全称セレクターの指定より優先されます。important宣言や他のオリジンを含むカスケード全体をこの例だけで説明することはできません。全称セレクターに同じプロパティ名が存在していても、ブラウザーはインラインスタイルの &lt;code&gt;box-sizing: content-box&lt;/code&gt; を採択します。&lt;/p&gt;
&lt;p&gt;Stylesタブで取り消し線を確認後、Computedタブで &lt;code&gt;box-sizing&lt;/code&gt; を検索すると、算出値として &lt;code&gt;content-box&lt;/code&gt; が表示されます。Stylesタブで上書きの事実を確認し、Computedタブで現在有効な値を確かめる手順を踏みます。なお、本教材では画面上のボタン操作によってプロパティ値を切り替えて検証できます。&lt;/p&gt;
&lt;h3&gt;スタイル適用の確認手順&lt;/h3&gt;
&lt;p&gt;スタイルの適用を判断する際は、次の要因を確認します。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;要素の選択&lt;/strong&gt;：現在調査しているDOMノードが対象の要素そのものであるか。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;詳細度とカスケード&lt;/strong&gt;：より詳細度の高いセレクターやインラインスタイルによって上書きされていないか。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;プロパティの文脈依存性&lt;/strong&gt;：インライン要素に対する &lt;code&gt;width&lt;/code&gt; のように、要素の &lt;code&gt;display&lt;/code&gt; 形式によってプロパティ自体が無視されていないか。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;継承されるプロパティと継承されないプロパティ&lt;/h3&gt;
&lt;p&gt;ある要素に直接指定がなくても、親要素のプロパティ値が引き継がれる場合があります。本教材の &lt;code&gt;demo/index.html&lt;/code&gt; では、&lt;code&gt;body&lt;/code&gt; 要素に対して次の指定が行われています。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* demo/index.html の抜粋 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#edf1f2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  color&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#182b34&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  font&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;17&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.75&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; system-ui&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;div id=&quot;box&quot;&amp;gt;&lt;/code&gt; 要素自体には &lt;code&gt;color&lt;/code&gt; や &lt;code&gt;font&lt;/code&gt; の指定がありませんが、画面上の文字には &lt;code&gt;#182b34&lt;/code&gt; とシステムフォントが適用されます。Stylesタブで &lt;code&gt;&amp;lt;div id=&quot;box&quot;&amp;gt;&lt;/code&gt; を選択すると、&lt;code&gt;Inherited from body&lt;/code&gt; の下に上記の定義が表示されます。&lt;/p&gt;
&lt;p&gt;一方で、&lt;code&gt;box-sizing&lt;/code&gt;、&lt;code&gt;width&lt;/code&gt;、&lt;code&gt;margin&lt;/code&gt;、&lt;code&gt;padding&lt;/code&gt;、&lt;code&gt;border&lt;/code&gt; といったボックスモデルを構成するプロパティは初期状態では親要素から継承されません。&lt;/p&gt;
&lt;h3&gt;値の切り分け手順&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;確認順序&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;観察する対象&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;参照するタブ&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;判断する基準&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;1&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;選択中のDOMノード&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Elements ツリー&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;調べたい対象要素そのものがハイライトされているか&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;2&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;インラインスタイルの有無&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Styles (&lt;code&gt;element.style&lt;/code&gt;)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;style&lt;/code&gt; 属性で直接プロパティが上書きされていないか&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;3&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;当該プロパティの宣言行&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Styles&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;宣言に取り消し線が引かれていないか&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;4&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;親要素からの継承&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Styles (&lt;code&gt;Inherited from...&lt;/code&gt;)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;プロパティが親から引き継がれたものか、直接の指定か&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;5&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;最終計算値&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Computed&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;単位換算を含め、描画に使われている値は何か&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;4. ボックスモデルを数値で説明する&lt;/h2&gt;
&lt;p&gt;Webページのレイアウト崩れを調べる際、CSSで指定した数値とブラウザーが画面上に割り当てた幅の食い違いを解消するには、要素の大きさを構成する各領域を個別の数値に分解し、どのような規則で加算されているかを数式で確かめる必要があります。本章では、CSSのボックスモデルを構成する各層の役割を整理し、教材ソースの数値と教材画面の測定結果を照合しながら幅の計算根拠を解説します。&lt;/p&gt;
&lt;h3&gt;画面上の占有幅を構成する四つの領域&lt;/h3&gt;
&lt;p&gt;ブラウザーがHTML要素を描画する際、通常のCSSレイアウトでは、要素が生成する箱の寸法をもとに配置が決まります。この大きさと位置を決める仕組みが&lt;strong&gt;ボックスモデル&lt;/strong&gt;です。ボックスモデルは内側から順に、テキストや画像が配置される&lt;strong&gt;コンテンツ領域&lt;/strong&gt;、余白の&lt;strong&gt;padding&lt;/strong&gt;、境界線の&lt;strong&gt;border&lt;/strong&gt;、他の要素との距離を保つ&lt;strong&gt;margin&lt;/strong&gt;の四つの領域で構成されます。&lt;/p&gt;
&lt;p&gt;スタイルシートに記述した &lt;code&gt;width&lt;/code&gt; プロパティがどの領域を制御するかを決定するのが &lt;strong&gt;&lt;code&gt;box-sizing&lt;/code&gt;&lt;/strong&gt; プロパティです。初期値の &lt;code&gt;content-box&lt;/code&gt; と、広く使われる &lt;code&gt;border-box&lt;/code&gt; のどちらを指定するかで、外見上の幅とCSSの指定値との対応関係が変化します。&lt;/p&gt;
&lt;h3&gt;教材の構造と二つの幅の現れ方&lt;/h3&gt;
&lt;p&gt;教材ファイル &lt;code&gt;demo/index.html&lt;/code&gt; では、ボックスモデルの挙動を確認するための領域が次のように定義されています。なお、本教材はHTTPサーバー経由（例: &lt;code&gt;http://127.0.0.1:9033/demo/&lt;/code&gt;）でHTMLとJSONの二つのファイルを配信して動作させます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;box-sizing:content-box&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;カードの内容領域&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;measure&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* demo/index.html の抜粋 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.box&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;320&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; solid&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; #177887&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#dceff2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;幅に &lt;code&gt;width: 320px&lt;/code&gt;、余白に &lt;code&gt;padding: 24px&lt;/code&gt;、枠線に &lt;code&gt;border: 4px solid #177887&lt;/code&gt;、外側余白に &lt;code&gt;margin: 16px 0&lt;/code&gt; が宣言されています。ボタン操作によりインラインスタイルの &lt;code&gt;box-sizing&lt;/code&gt; が切り替わり、&lt;code&gt;getComputedStyle(box).boxSizing&lt;/code&gt; による計算値と &lt;code&gt;box.getBoundingClientRect().width&lt;/code&gt; による描画幅が画面下に反映されます。&lt;/p&gt;
&lt;p&gt;初期状態（&lt;code&gt;content-box&lt;/code&gt;）の表示が次の画像です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/2c965531e67b3ee2/images/02-content-box.png&quot; alt=&quot;content-boxでは枠線を含む幅が376pxになった&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;計測結果には「box-sizing: content-box ／ 枠線を含む幅: 376px」と出力されます。&lt;code&gt;width: 320px&lt;/code&gt; の指定に対し、枠線を含めた横幅は &lt;code&gt;376px&lt;/code&gt; に達しています。&lt;/p&gt;
&lt;h3&gt;376pxと320pxの内訳を計算で解明する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;box-sizing: content-box&lt;/code&gt; において、&lt;code&gt;width&lt;/code&gt; はコンテンツ領域の幅を指定します。左右のpaddingとborderは指定幅の外側に加算されます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;320 + 24 × 2 + 4 × 2 = 376&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;コンテンツ幅 &lt;code&gt;320px&lt;/code&gt; に左右padding &lt;code&gt;48px&lt;/code&gt; と左右border &lt;code&gt;8px&lt;/code&gt; が加わり、合計 &lt;code&gt;376px&lt;/code&gt; となります。&lt;/p&gt;
&lt;p&gt;続いて教材画面の「border-box にする」ボタンを押した状態を確認します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/2e85b291e4760b8b/images/03-border-box.png&quot; alt=&quot;border-boxへ変更すると枠線を含む幅が320pxになった&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;表示は「box-sizing: border-box ／ 枠線を含む幅: 320px」へ変化します。&lt;code&gt;box-sizing: border-box&lt;/code&gt; では、&lt;code&gt;width&lt;/code&gt; がコンテンツ領域、padding、borderを合わせた枠線外側までの幅を指します。左右のborderとpaddingの合計 &lt;code&gt;56px&lt;/code&gt; を差し引いた残りがコンテンツ幅です。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;320 - (24 × 2) - (4 × 2) = 264&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;コンテンツ幅は &lt;code&gt;264px&lt;/code&gt; となります。&lt;/p&gt;
&lt;h3&gt;DevToolsでボックスモデルの各層を照合する&lt;/h3&gt;
&lt;p&gt;Chrome DevToolsのElementsパネルにあるStylesタブにはボックスモデルの図が表示されます。&lt;a href=&quot;https://developer.chrome.com/docs/devtools/css&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome公式のCSS確認機能ガイド&lt;/a&gt;によると、Stylesペインのボックスモデル図からpadding、border、marginの各数値を確認でき、プロパティ値を一時編集して表示変化を試すことができます。&lt;/p&gt;
&lt;p&gt;DevToolsで追試する場合は、コンテンツ幅とpadding、border、marginを別々に確認します。この教材ではpaddingは四辺24px、borderは四辺4px、marginは上下16px、左右0です。色や表示位置はUIの版により異なるため、ラベルと数値を照合してください。&lt;/p&gt;
&lt;h3&gt;marginの性質とgetBoundingClientRectの測定対象&lt;/h3&gt;
&lt;p&gt;marginは要素の境界線の外側に作られる間隔であり、要素自体の寸法には加算されません。背景色や枠線が描画されるのはborderの内側までで、margin領域には親要素の背景が透けます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect&quot; rel=&quot;noopener noreferrer&quot;&gt;CSSOM View仕様&lt;/a&gt;で定義される &lt;code&gt;getBoundingClientRect()&lt;/code&gt; メソッドは、要素のボーダーボックスを基準とした矩形の大きさとビューポートに対する相対位置を返すため、marginは含まれません。また、&lt;code&gt;getBoundingClientRect()&lt;/code&gt; はCSSの &lt;code&gt;transform&lt;/code&gt; による変形適用後の最終的な描画境界を取得します。例えば &lt;code&gt;transform: scale(2)&lt;/code&gt; がある場合、レイアウト幅が &lt;code&gt;320px&lt;/code&gt; でも戻り値は変形後の &lt;code&gt;640px&lt;/code&gt; になります。実観測の限界として、スクリプト取得値がレイアウト上の寸法なのか変形後の描画境界なのかはコードと併せて識別する必要があります。本教材には &lt;code&gt;transform&lt;/code&gt; 指定がないため、測定値 &lt;code&gt;376px&lt;/code&gt; と &lt;code&gt;320px&lt;/code&gt; は純粋なボックスモデルの計算規則を反映しています。&lt;/p&gt;
&lt;h3&gt;数値と見え方を往復する調査手順&lt;/h3&gt;
&lt;p&gt;調査の過程を整理する手段として、次の表に、ソースから計算した値と画面に表示された測定値を分けて記します。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;項目&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;code&gt;content-box&lt;/code&gt; 設定時&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;code&gt;border-box&lt;/code&gt; 設定時&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;備考&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;width&lt;/code&gt; のCSS指定値&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;320px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;320px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;スタイルシートに記述された値&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;左 / 右 padding&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;24px / 24px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;24px / 24px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;内側の余白（合計 48px）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;左 / 右 border&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;4px / 4px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;4px / 4px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;枠線の太さ（合計 8px）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;計算上のコンテンツ幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;320px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;264px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;テキストが配置される内側の幅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;枠線を含む外見幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;376px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;320px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;getBoundingClientRect() の測定値&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;外見幅の計算根拠&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;320 + 48 + 8 = 376&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;320&lt;/code&gt; （固定）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;差分 56px の扱いによる違い&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;演習：意図したレイアウトへの改善提案&lt;/h3&gt;
&lt;p&gt;全体の幅を &lt;code&gt;320px&lt;/code&gt; に保ちつつコンテンツ幅を &lt;code&gt;280px&lt;/code&gt; に広げる場合、paddingとborderの合計を &lt;code&gt;320 - 280 = 40px&lt;/code&gt; に抑える必要があります。枠線 &lt;code&gt;4px&lt;/code&gt;（左右計 &lt;code&gt;8px&lt;/code&gt;）を変更しない場合、左右paddingの合計は &lt;code&gt;40 - 8 = 32px&lt;/code&gt; となり、片側 &lt;code&gt;16px&lt;/code&gt; に設定します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* 改善案の提示（※提案コード。未実行） */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.box-adjusted&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  box-sizing&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border-box&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;320&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  border&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; solid&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; #177887&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この指定により、全体の幅は &lt;code&gt;320px&lt;/code&gt; のままコンテンツ幅が &lt;code&gt;320 - (16 × 2) - (4 × 2) = 280px&lt;/code&gt; となります。&lt;/p&gt;
&lt;h2&gt;5. はみ出すカードから固定幅を見つける&lt;/h2&gt;
&lt;p&gt;画面の幅が狭くなったとき、配置したカード要素が枠線を突き抜けて右側へ飛び出してしまう現象に遭遇することがあります。本章では、狭い枠組みの中で意図せずはみ出してしまうカード要素を起点として、どの指定が寸法を縛っているのかを特定し、流動的な幅へ安全に整える調査手順を解説します。&lt;/p&gt;
&lt;h3&gt;突き抜けた要素とスクロールの発生&lt;/h3&gt;
&lt;p&gt;内容の寸法が親の領域を超えたとき、はみ出した部分がどう見えるかはoverflowなどの指定によって変わります。この教材では親が表示領域を制限しているため、文章の一部が境界で切れる様子を調べます。&lt;/p&gt;
&lt;p&gt;観察には、HTTPサーバーを介してHTMLとJSONを配信する検証環境（&lt;code&gt;http://127.0.0.1:9033/demo/&lt;/code&gt;）を用います。通常のデスクトップ版Chromeの表示領域内に配置した360pxの固定枠の中で何が起きているのかを追います。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;frame&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;art&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-hidden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;☕&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;朝のコーヒー&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;長い説明文でも内容を読めるカードを作ります。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* demo/index.html の抜粋 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.frame&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;360&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;max-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; dashed&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; #9b6848&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;overflow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;auto&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.fixed&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;520&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#e3eff3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この枠組みでは、茶色い破線の境界を持つ親要素&lt;code&gt;.frame&lt;/code&gt;の内側に、商品情報を提示するカード要素&lt;code&gt;#product&lt;/code&gt;が配置されています。初期状態の画面を表示させた様子が次の図です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/b0255d15c5d72de5/images/04-overflow.png&quot; alt=&quot;幅520pxのカードが教材のコンテナからはみ出す&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;薄い青色のカードは親の内容幅を超え、右側の文章が表示領域の境界で切れています。この画像には水平スクロールバーは明瞭に写っていません。コード上は横方向にスクロールできる領域ですが、スクロール操作そのものは今回確認していません。枠の境界線とカードの終端が食い違っており、右端に追いやられた文字列はスクロールを動かさなければ読めない状態です。&lt;/p&gt;
&lt;h3&gt;親要素の内容幅とカードの固定幅を計算する&lt;/h3&gt;
&lt;p&gt;はみ出しが発生している理由を説明するために、親要素がカードを受け入れられる有効な寸法（内容幅）を算出します。&lt;/p&gt;
&lt;p&gt;教材のスタイルシートでは、全要素に対して&lt;code&gt;box-sizing: border-box&lt;/code&gt;が適用されています。したがって、&lt;code&gt;.frame&lt;/code&gt;に指定された&lt;code&gt;width: 360px&lt;/code&gt;という値は、左右の枠線とパディングを含んだ外側の合計値となります。CSSの記述から数値を拾い出すと、左右のボーダー幅はそれぞれ2pxであり、左右のパディングはそれぞれ12pxです。ここから親要素が内側の要素に割り当てられる内容幅（content boxの幅）を逆算します。&lt;/p&gt;
&lt;p&gt;親要素の全体幅360pxから左右のボーダー幅（2px × 2 = 4px）を引き、さらに左右のパディング幅（12px × 2 = 24px）を差し引きます。この減算により、360px - 4px - 24px = 332pxという数値が導かれます。つまり、親要素が子要素を収納できる横幅は実質的に332pxです。&lt;/p&gt;
&lt;p&gt;それに対して、子要素である&lt;code&gt;#product&lt;/code&gt;には&lt;code&gt;.fixed&lt;/code&gt;クラスが付与されており、&lt;code&gt;width: 520px&lt;/code&gt;という固定値が宣言されています。親が差し出せる332pxの空間に対して、子が520pxの占有を要求しているため、差分である188px分だけ親の内容領域から溢れ出します。この直接の破綻を踏まえ、Stylesペインから幅を絶対値で固定している宣言がないかを確かめる作業が初動となります。&lt;/p&gt;
&lt;h3&gt;overflowプロパティが作るスクロール領域の役割&lt;/h3&gt;
&lt;p&gt;親要素の定義に含まれている&lt;code&gt;overflow: auto&lt;/code&gt;は、内容が枠をはみ出した際にブラウザーが自動的にスクロールバーを生成し、境界外の領域を閲覧可能にする指定です。&lt;/p&gt;
&lt;p&gt;スクロールバーの出現により文字や画像が画面外へ完全に消失する事態は免れていますが、これは親要素がはみ出しを局所的に封じ込めた結果にすぎず、カード自体が不適切な寸法を保ったまま押し込まれている事実に変わりはありません。固定幅のままでスクロール領域に依存すると、読み手は横方向へのスクロール操作を強いられます。&lt;code&gt;overflow: auto&lt;/code&gt;は過剰な幅を要求している原因そのものを解消する指定ではないため、スクロールバーの存在を手がかりとして受け止めつつ、過剰な幅を持つ子要素の特定へ進める必要があります。&lt;/p&gt;
&lt;h3&gt;固定幅から百分率と上限値への移行&lt;/h3&gt;
&lt;p&gt;原因が&lt;code&gt;width: 520px&lt;/code&gt;という固定値にあることが判明したため、次はカードの寸法指定を親要素の幅に適応させる修正を検討します。教材画面の「親の幅に収める」ボタンを押すと、カードに適用されるクラスが&lt;code&gt;.fixed&lt;/code&gt;から&lt;code&gt;.responsive&lt;/code&gt;へと切り替わります。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* demo/index.html の抜粋 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.responsive&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;max-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;520&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#e3eff3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このCSS定義では、基本の幅として&lt;code&gt;width: 100%&lt;/code&gt;を指定しつつ、追加で&lt;code&gt;max-width: 520px&lt;/code&gt;という上限値を設けています。親要素の内容幅が520pxを下回る狭い領域では&lt;code&gt;width: 100%&lt;/code&gt;が働き、カードの幅は親の内容幅である332pxに一致するよう縮小されます。逆に、親要素が十分に広い大画面に置かれた場合は&lt;code&gt;max-width: 520px&lt;/code&gt;が歯止めとなり、カードが必要以上に横に間延びすることを防ぎます。&lt;/p&gt;
&lt;p&gt;切り替えボタンを押した直後の教材画面の様子が次の図です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/9004dd35a621e8cd/images/05-fit.png&quot; alt=&quot;幅を100%へ変更したカード内で文章が折り返す&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;カードの右端が親要素の右内枠（パディングの手前）で揃い、外枠の破線を越えなくなっています。カード内の説明文「長い説明文でも内容を読めるカードを作ります。」がカードの横幅に合わせて適切に折り返されています。&lt;/p&gt;
&lt;h3&gt;要素の寸法と表示状態の比較&lt;/h3&gt;
&lt;p&gt;二つの状態について、ソースから計算した寸法と画面上の挙動を整理します。カードの実効幅はコードからの計算値であり、独立した測定ログを採った値ではありません。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;観測対象&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;固定幅の初期状態（.fixed）&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;改善後の流動状態（.responsive）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;親枠（.frame）の全体幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;360px（border-box）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;360px（border-box）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;親枠の左右ボーダー合計&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;4px（左2px + 右2px）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;4px（左2px + 右2px）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;親枠の左右パディング合計&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;24px（左12px + 右12px）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;24px（左12px + 右12px）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;親枠の有効内容幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;332px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;332px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;カード（#product）の指定幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;width: 520px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;width: 100%; max-width: 520px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;カードの実効幅&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;520px&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;332px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;親の内容幅との差分&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;+188px（過剰）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;0px（過不足なし）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;横方向の状態&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;右側の内容が表示領域で切れる&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;掲載文が枠内に収まる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;テキストの改行動作&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;右端で延長し一部が不可視領域へ進入&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;枠内で自動的に折り返し完了&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;外枠コンテナの寸法や余白設定は一切変更していません。内側に配置されたカードが親の内容幅332pxを尊重する指定に改められただけで、レイアウト全体の整合性が回復しています。&lt;/p&gt;
&lt;h3&gt;内部レイアウトの破綻を防ぐ追加調査&lt;/h3&gt;
&lt;p&gt;カード全体の幅を整えたあとも、カード内部に配置されたFlexboxコンテナや予期せぬ文字列の流入が引き起こす問題について切り分けの視点を持っておく必要があります。&lt;/p&gt;
&lt;p&gt;Elementsパネルでは、要素がFlexboxレイアウトを形成している場合にDOMツリー上で専用のバッジが表示されます。&lt;a href=&quot;https://developer.chrome.com/docs/devtools/css/flexbox&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevToolsのFlexbox解説&lt;/a&gt;にあるとおり、Elementsパネルのflexバッジをクリックするとオーバーレイが描画され、アイテムの並び順や余白の配置状況を検査できます。&lt;/p&gt;
&lt;p&gt;カードの内部構造を確認すると、アイコン表示領域&lt;code&gt;.art&lt;/code&gt;とテキスト群を包む領域が、親の&lt;code&gt;.product&lt;/code&gt;によって横並びに配置されています。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;art&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-hidden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;☕&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;朝のコーヒー&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;長い説明文でも内容を読めるカードを作ります。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* demo/index.html の抜粋 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.product&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;gap&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;18&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;align-items&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.art&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;160&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;linear-gradient&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;140&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;deg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#d9b467&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#eddbaf&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border-radius&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;grid&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;place-items&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;font-size&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;56&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この横並び配置において、説明文の中に極端に長い英単語や途切れのないURL記号列が紛れ込んだ場合、新たなはみ出しが発生する懸念があります。Flexアイテムの横幅の最小値を示す&lt;code&gt;min-width&lt;/code&gt;プロパティは初期値が&lt;code&gt;auto&lt;/code&gt;であり、折り返せない長い文字列が存在すると文字列の長さを優先してアイテムを押し広げてしまいます。&lt;/p&gt;
&lt;p&gt;左側の&lt;code&gt;.art&lt;/code&gt;には160pxが指定されていますが、flexアイテムとして縮む場合があります。gapの18pxも含め、絵と文章が限られた幅を分け合います。このようなはみ出しを防ぐ机上の追加対策例として、以下の2行が考えられます（教材内では未実行の提案コードです）。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* 机上の追加対策例（教材内では未実行の提案コード） */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.product&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt; div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;:last-child&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  min-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  overflow-wrap&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: anywhere;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;min-width: 0&lt;/code&gt;はFlexアイテムが親の許容幅まで縮小（flex収縮）できるように制限を解除する指定です。&lt;code&gt;overflow-wrap: anywhere&lt;/code&gt;は改行位置のない連続文字列であっても枠線の手前で強制的に改行を発生させる指定です。この案は長い文字列によるはみ出しを減らすための候補です。内容と幅を変えた追試で効果を確かめる必要があります。&lt;/p&gt;
&lt;h2&gt;6. 商品データの取得を画面と結び付ける&lt;/h2&gt;
&lt;p&gt;Webアプリケーションの不具合調査では、画面に文字が現れない原因が通信自体の失敗なのか、受信後の処理にあるのかを取り違えやすくなります。ここではローカルの検証用ページを用いて、ボタン操作から画面に商品データが反映されるまでの過程を分解し、観測の基準を整理します。&lt;/p&gt;
&lt;h3&gt;画面の変化と三つの成否基準&lt;/h3&gt;
&lt;p&gt;ユーザーが「商品データを取得」ボタンを押してから画面にデータが表示されるまでの処理は、大きく三つの段階に整理できます。第一がサーバーとHTTP通信を行って応答を受け取る&lt;strong&gt;応答成功&lt;/strong&gt;、第二が受け取ったテキストをオブジェクトへ変換する&lt;strong&gt;parse成功&lt;/strong&gt;、第三が取り出した値をもとにDOM要素を書き換える&lt;strong&gt;描画成功&lt;/strong&gt;です。&lt;/p&gt;
&lt;p&gt;画面に文字が出ない現象に対して、通信が失敗していると即断してはいけません。通信自体はHTTPステータス200で成功していても、その後のJSON解析で構文エラーが起きれば描画には届きません。逆にJSON解析が進んでも、反映先要素の指定が誤っていれば画面には表示されません。まずは通信経路の境界線で起きた事象を確かめ、次にプログラム内部の解釈を確かめる順序で切り分けます。&lt;/p&gt;
&lt;h3&gt;教材の通信コードとデータ構造&lt;/h3&gt;
&lt;p&gt;本記事の教材は、HTML（&lt;code&gt;demo/index.html&lt;/code&gt;）とJSON（&lt;code&gt;product.json&lt;/code&gt;）の2ファイルをHTTPサーバーで配信して動作させます。この教材の成功例を再現するには、HTMLとは別にproduct.jsonを配信する必要があります。観察対象となるスクリプトの処理構造を確認します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// demo/index.htmlの抜粋&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(mode&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;network&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#networkResult&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;取得中です。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;result&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(path,{cache:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;no-store&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response.ok){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        out.textContent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`HTTP ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}：データを取得できませんでした。URLを確認してください。`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        out.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      out.textContent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`HTTP ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}：${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ／ 在庫 ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;stock&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} 個`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      out.textContent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;応答を受け取れませんでした。接続状態を確認してください。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      out.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#success&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onclick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;product.json&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#failure&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onclick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;missing-product.json&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ボタンをクリックするとDOMの文言が「取得中です。」へ更新され、その後に &lt;code&gt;fetch()&lt;/code&gt; による非同期通信が開始されます。リクエスト対象の &lt;code&gt;product.json&lt;/code&gt; は次の形式です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;朝のコーヒー&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;stock&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;スクリプトは &lt;code&gt;await response.json()&lt;/code&gt; で文字列を解析し、得られたオブジェクトから &lt;code&gt;data.name&lt;/code&gt; と &lt;code&gt;data.stock&lt;/code&gt; の値を取り出します。最後に &lt;code&gt;#networkResult&lt;/code&gt; 要素の &lt;code&gt;textContent&lt;/code&gt; へ、ステータス番号と結合した文字列を代入して描画を完了します。&lt;/p&gt;
&lt;h3&gt;教材ページにおける正常表示の観測&lt;/h3&gt;
&lt;p&gt;HTTPサーバーを起動し、macOS上のChromeでローカル教材ページ（&lt;code&gt;http://127.0.0.1:9033/demo/?case=network&lt;/code&gt;）を開いて「商品データを取得」ボタンを押したときの画面出力を確認します。次の画像は、教材画面で取得処理が完了した状態を記録したものです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/83dff0fd5b3e92e7/images/08-network-ok.png&quot; alt=&quot;商品データを取得しHTTP200と在庫12個を表示&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ボタン下部の領域に「HTTP 200：朝のコーヒー ／ 在庫 12 個」という緑枠のメッセージが表示されます。この表示から、通信が応答を返し、スクリプトがJSONを解釈して枠内に出力した結果が読み取れます。ただし、この画面表示だけでは通信の中身そのものを直接観察したわけではないという限界があります。通信の一次情報に直接触れて裏付けを取る必要があります。&lt;/p&gt;
&lt;h3&gt;Networkパネルを使った追試手順&lt;/h3&gt;
&lt;p&gt;画面上の結果を客観的事実と照合するため、Chrome DevToolsのNetworkパネルを利用します。公式の解説である&lt;a href=&quot;https://developer.chrome.com/docs/devtools/network&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevTools Networkガイド&lt;/a&gt;でも、通信を記録して個別の要求のヘッダーや応答内容を確かめる基本手順が示されています。&lt;/p&gt;
&lt;p&gt;追試は次の順序で進めます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;教材ページを開いた状態でDevToolsを立ち上げ、「Network」タブを選択します。&lt;/li&gt;
&lt;li&gt;左上の記録ボタンが赤色になり、通信の記録状態になっていることを確認します。&lt;/li&gt;
&lt;li&gt;必要に応じて「Clear」ボタンを押し、既存のログを消去します。&lt;/li&gt;
&lt;li&gt;画面上の「商品データを取得」ボタンをクリックします。&lt;/li&gt;
&lt;li&gt;Networkパネルのリクエスト一覧に現れた &lt;code&gt;product.json&lt;/code&gt; をクリックします。&lt;/li&gt;
&lt;li&gt;右側の詳細画面で「Headers」タブと「Response」タブを開き、結果を検査します。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;「Headers」ではリクエストURLやステータスコードが &lt;code&gt;200 OK&lt;/code&gt; であるかを確認できます。「Response」タブではサーバーから返された応答本文のテキストを確認でき、&lt;code&gt;{&quot;name&quot;:&quot;朝のコーヒー&quot;,&quot;stock&quot;:12}&lt;/code&gt; が届いているかを検証できます。&lt;/p&gt;
&lt;h3&gt;押した瞬間と表示時点の時間差を追う&lt;/h3&gt;
&lt;p&gt;非同期通信では、ボタンがクリックされてからサーバーが応答を返し、ブラウザーが処理するまでに物理的な時間がかかります。教材コードでも &lt;code&gt;load()&lt;/code&gt; の先頭で &lt;code&gt;out.textContent = &apos;取得中です。&apos;&lt;/code&gt; を実行しています。コード上ではこの中間の文字列をDOMへ代入します。ただし、通信が速ければブラウザーが画面へ描く前に最終表示へ変わることがあり、人の目で必ず見えるとは限りません。今回、取得中の状態は撮影していません。&lt;/p&gt;
&lt;p&gt;ボタンを押しても画面が変わらない場合、クリックイベントの未発火と即断せず、Networkパネルで要求が送信中（Pending）か完了済みかを確認します。時間軸に沿って状態の遷移を追うことが重要です。&lt;/p&gt;
&lt;h3&gt;三つの段階を切り分ける調査手順&lt;/h3&gt;
&lt;p&gt;画面に意図したデータが出ないときは、境界線を意識して次の表に沿って調査を進めます。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;調査の着眼点&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;観察する場所&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;期待される正常な状態&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;この段階が失敗している場合の例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;応答成功&lt;/strong&gt;の確認&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;DevToolsのNetworkパネル（Headers）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ステータスコードが200番台で返る&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;404 Not Found、500エラー、通信の切断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;parse成功&lt;/strong&gt;の確認&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;NetworkパネルのResponseとConsole&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;有効なJSONがオブジェクト化される&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;JSONの文法誤りによるSyntaxErrorの発生&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;描画成功&lt;/strong&gt;の確認&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Elementsパネルおよび画面上の表示&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;取り出した値が目的のDOM要素に入る&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;代入先要素が存在しない、プロパティ名の指定誤り&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;最初の切り分けでは表の上から順に見る方法が使えます。大元の応答が404エラーであったりJSONが破損していたりする場合、描画処理側のコードを修正しても解決しないためです。&lt;/p&gt;
&lt;p&gt;なお、表示崩れや古いスクリプトの残存が疑われる場合でも、任意のCSSやJSを再読み込みすれば必ず復旧するわけではありません。本教材においては、ボタン操作によって再度通信と描画の関数を実行することで状態の変化を再検証できます。&lt;/p&gt;
&lt;h3&gt;発展的な確認のための演習&lt;/h3&gt;
&lt;p&gt;挙動の変化を観察するために、次の机上の検証コードを例として挙げます（実際の動作記録ではありません）。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 机上の検証用コード（未実行の例）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 応答成功とparse成功の境目を観察するために、構文の崩れた文字列を扱う例&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; testParseBoundary&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; dummyResponse&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;{&quot;name&quot;: &quot;壊れたデータ&quot;, stock: }&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dummyResponse.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;parse成功:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (error) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;parse失敗を検知:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, error);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この例では、レスポンスの生成自体が成立していても、&lt;code&gt;dummyResponse.json()&lt;/code&gt; の呼び出し時に構文解析エラーが発生して &lt;code&gt;catch&lt;/code&gt; ブロックへ分岐します。&lt;/p&gt;
&lt;p&gt;また、教材の &lt;code&gt;product.json&lt;/code&gt; のキーを &lt;code&gt;{&quot;title&quot;: &quot;朝のコーヒー&quot;, &quot;quantity&quot;: 12}&lt;/code&gt; に変更すると、通信とJSONパースは成功しますが、スクリプトが &lt;code&gt;data.name&lt;/code&gt; を参照するため画面には &lt;code&gt;undefined&lt;/code&gt; が表示されます。表示の不具合がすべてHTMLとJavaScriptの差異のみから生じるわけではなく、データ構造の不一致など多様な原因が存在します。&lt;/p&gt;
&lt;h2&gt;7. 404と接続失敗を混同しない&lt;/h2&gt;
&lt;h3&gt;画面の赤色と通信の成否を分ける&lt;/h3&gt;
&lt;p&gt;ウェブアプリケーションで赤い警告枠が表示されても、通信自体が切断されたりサーバーが停止したりしたとは限りません。画面の警告表示と、ネットワーク層で起きた現象は区別する必要があります。Chrome DevToolsの&lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/overview&quot; rel=&quot;noopener noreferrer&quot;&gt;Networkパネルの概要&lt;/a&gt;でも示されているように、通信の事実を確かめる作業が欠かせません。&lt;/p&gt;
&lt;p&gt;教材（&lt;code&gt;http://127.0.0.1:9033/demo/&lt;/code&gt;）の通信パネルにある「存在しないデータを取得」ボタンを押すと、画面上には薄赤い背景でメッセージが現れます。この挙動をローカル環境のmacOS版Chromeで操作して記録した様子が次の画面です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/c8206fa972b9da47/images/09-network-404.png&quot; alt=&quot;存在しないデータへのHTTP404を案内する画面&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;画面には「HTTP 404：データを取得できませんでした。URLを確認してください。」と表示されます。この結果は通信が途絶したわけでも、JavaScriptの非同期処理が例外を投げて異常終了したわけでもありません。要求はローカルサーバーへ正常に届き、サーバーから404というステータスコードを伴ったHTTP応答が返ってきた結果として表示されています。&lt;/p&gt;
&lt;h3&gt;fetchにおけるrejectとHTTPエラー応答の差&lt;/h3&gt;
&lt;p&gt;JavaScriptの&lt;code&gt;fetch&lt;/code&gt;関数を扱う際、HTTPステータスが400番台や500番台であればPromiseが自動的にrejectされると誤認されることがあります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch&lt;/code&gt;が返すPromiseがrejectされる条件は、端末のオフライン、名前解決の失敗、接続タイムアウトなど、通信上の失敗に加え、不正な要求や中断などでも起こります。サーバーと接続し、相手からHTTP応答ヘッダーを受け取った時点で、その応答が&lt;code&gt;404 Not Found&lt;/code&gt;や&lt;code&gt;500 Internal Server Error&lt;/code&gt;であっても、Promiseはresolveされます。HTTPのエラーステータスとPromiseのrejectは別の分類です。&lt;a href=&quot;https://fetch.spec.whatwg.org/#fetch-method&quot; rel=&quot;noopener noreferrer&quot;&gt;Fetch Standard&lt;/a&gt;でも応答処理と中断などの失敗処理が区別されています。&lt;/p&gt;
&lt;p&gt;教材スクリプト&lt;code&gt;demo/index.html&lt;/code&gt;の該当箇所を確認します。なお、本教材はHTMLとJSONの二ファイルをHTTPサーバーで配信する構成をとっています。&lt;/p&gt;
&lt;p&gt;通信の章で掲載したload関数では、fetchの直後にresponse.okを調べ、偽なら表示を更新してreturnしています。以下ではその分岐を追います。&lt;/p&gt;
&lt;p&gt;「存在しないデータを取得」ボタンを押すと引数&lt;code&gt;path&lt;/code&gt;に&lt;code&gt;missing-product.json&lt;/code&gt;が渡されます。サーバー上に配置されていないため404が返りますが、実行の流れは直ちに&lt;code&gt;catch&lt;/code&gt;ブロックへ移りません。&lt;code&gt;response&lt;/code&gt;変数に&lt;code&gt;Response&lt;/code&gt;オブジェクトが格納され、&lt;code&gt;if(!response.ok)&lt;/code&gt;が評価されます。&lt;/p&gt;
&lt;h3&gt;response.okによる明示的な遮断と早期return&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;response.ok&lt;/code&gt;プロパティは、HTTPステータスコードが200から299までの範囲にある場合に&lt;code&gt;true&lt;/code&gt;となり、それ以外では&lt;code&gt;false&lt;/code&gt;を返します。教材コードでは404応答を受け取った際に&lt;code&gt;false&lt;/code&gt;となるため、&lt;code&gt;if&lt;/code&gt;文の本体が実行され、直後の&lt;code&gt;return&lt;/code&gt;で処理を中断します。&lt;/p&gt;
&lt;p&gt;もし早期リターンを置かずに続行させると、後続の&lt;code&gt;response.json()&lt;/code&gt;が呼び出されます。Webサーバーが返す404応答の本文はHTMLエラーページなどの場合があり、その状態で&lt;code&gt;response.json()&lt;/code&gt;を呼ぶと構文エラー（SyntaxError）が送出されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;サーバーの404応答（HTML本文）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;response.ok の検査を怠る&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;response.json() を実行する&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SyntaxError が発生して catch ブロックへ移る&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;画面に「通信失敗」と誤認表示される&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;404を受け取った際にメッセージを設定して直ちに&lt;code&gt;return&lt;/code&gt;しているのは、この構文解析エラーによる誤遷移を防ぐ設計です。&lt;/p&gt;
&lt;h3&gt;一つのcatchに集約される教材コードの限界&lt;/h3&gt;
&lt;p&gt;教材のコードでは、例外の受け皿が単一の&lt;code&gt;catch&lt;/code&gt;ブロックしかありません。この記述により、物理的な回線切断などによる「接続失敗」と、HTTP 200応答の本文破損による「JSON解析失敗」が区別されず、同じメッセージで扱われます。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;失敗の種類&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;発生箇所&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;通信の成否&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;教材コードでの画面表示&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;HTTP 404&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;サーバー応答&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;成功（404受信）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;「HTTP 404：データを取得できませんでした…」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;接続失敗&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;fetchの呼び出し&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;失敗（到達不能）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;「応答を受け取れませんでした…」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;JSON解析失敗&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;response.json()&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;成功（200受信）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;「応答を受け取れませんでした…」&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;通信自体は成功していても、JSON解析に失敗すると「応答を受け取れませんでした。接続状態を確認してください。」と表示されます。二つの原因を分離して扱う設計案を以下に示します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 原因を分離して画面に反映する実装案（※机上の例・未実行）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; loadDetailed&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#networkResult&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  out.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;取得中です。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  out.className &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;result&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    response &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(path, { cache: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;no-store&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (networkError) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;要求を完了できませんでした。接続状態や中断の有無を確認してください。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response.ok) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; `HTTP ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}：要求を処理できませんでした。`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; `HTTP ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}：${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ／ 在庫 ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;stock&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} 個`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (parseError) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;応答本文の読み取りまたは解析に失敗しました。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;観測事実と推測の境界を意識する&lt;/h3&gt;
&lt;p&gt;本稿の検証は、macOS上のChromeを用い、同一ホスト上で稼働するローカル教材ページを操作して得られた観測に基づいています。&lt;code&gt;missing-product.json&lt;/code&gt;への要求に対して404ステータスが返り、画面の条件分岐を経てメッセージが表示されたことまでは記録された事実です。&lt;/p&gt;
&lt;p&gt;一方、オリジン間リソース共有（CORS）違反による遮断、ブラウザーがオフラインの際の挙動、ネットワーク速度制限、実機モバイル端末や他社製ブラウザー間の挙動差については検証を行っていません。また、本教材の画面表示が崩れた場合の復旧についても、今回記録したボタン操作と表示結果に限られ、任意の環境での復旧を保証するものではありません。&lt;/p&gt;
&lt;h3&gt;404調査の手順を整理する&lt;/h3&gt;
&lt;p&gt;リソースが見つからない場合にとるべき論理的な作業手順は以下の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;要求URLの完全な一致を確認する&lt;/strong&gt;：大文字・小文字、拡張子、相対パスの基準位置のずれがないか点検します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPステータスコードを直接確認する&lt;/strong&gt;：JavaScriptの&lt;code&gt;catch&lt;/code&gt;に頼らず、通信記録からサーバーが返したエラーコードが404であることを確認します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;応答ヘッダーと本文の形式を確認する&lt;/strong&gt;：サーバーの&lt;code&gt;Content-Type&lt;/code&gt;ヘッダーを確認し、JSONを期待する処理にHTML等が返っていないかを確かめます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;フロントエンド側の分岐論理を照合する&lt;/strong&gt;：&lt;code&gt;response.ok&lt;/code&gt;の判定が行われているか、404受信後に本文の解析処理へそのまま突入していないかを確認します。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. フォームのエラーを入力欄と結び付ける&lt;/h2&gt;
&lt;p&gt;入力欄で不備が生じた際、画面のどこに問題があるのかを利用者が即座に把握し、迷わず修正へ移れる設計は欠かせません。
本章では、ローカル教材のフォーム画面を用いて、確認ボタンを押したときに起きる現象、属性による要素同士の結び付き、そして入力値の検証状態を調査する手順を整理します。&lt;/p&gt;
&lt;h3&gt;空欄時の検証とフォーカス移動の観察&lt;/h3&gt;
&lt;p&gt;教材画面のナビゲーションから「フォーム」を選ぶと、メールアドレスの入力欄と入力確認用のボタンが配置された領域が表示されます。
まず入力欄に何も文字を入れない空欄の状態で、「入力を確認」ボタンを押したときの挙動を確認します。
画面上には赤色を基調とした枠で「メールアドレスを入力してください。」という警告文が現れ、同時にカーソルが自動的に入力欄の中へ移動します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/85566dd076faead4/images/10-form-empty.png&quot; alt=&quot;空欄を確認した後の入力案内とフォーカス&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;入力欄の内部に薄い灰色で表示されている案内文はプレースホルダーと呼ばれ、入力形式の見本を示すものであり入力値そのものとしては扱われません。
未入力のまま送信を試みた際に、警告文の提示だけでなく入力欄へフォーカスを自動で戻す挙動は、利用者が次にどの場所へ向かうべきかを直感的に伝える役割を果たします。&lt;/p&gt;
&lt;h3&gt;教材のHTMLと検証スクリプトの構造&lt;/h3&gt;
&lt;p&gt;この動作がどのようなマークアップとスクリプトによって成立しているのか、教材のソースコードから読み解きます。
以下は &lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;practiceForm&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; novalidate&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;連絡先メールアドレス（教材用）&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-describedby&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;emailHelp formMessage&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; placeholder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;reader@example.test&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;emailHelp&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;架空のアドレスで練習できます。入力は外部に送信しません。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;入力を確認&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;formMessage&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; role&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;未確認です。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;送信イベント発生時の処理は、次のように記述されています。こちらも &lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(mode&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;form&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#practiceForm&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onsubmit&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    event.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;preventDefault&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; email&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#email&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#formMessage&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; valid&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;email.validity.valid;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    email.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setAttribute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;aria-invalid&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;valid));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;valid&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;result&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;result bad&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    out.textContent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;valid&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;入力形式を確認しました。サーバーへの送信はしていません。&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;email.validity.valueMissing&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;メールアドレスを入力してください。&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;メールアドレスの形式を確認してください。&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;valid)email.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;focus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この実装において、&lt;code&gt;form&lt;/code&gt; タグには &lt;code&gt;novalidate&lt;/code&gt; 属性が指定されています。
この属性を付与するとブラウザー固有のポップアップ通知は抑止されますが、ブラウザーが備える制約の判定機能まで止まるわけではありません。
&lt;a href=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-constraint-validation-api&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG HTML Standardの制約検証APIの仕様&lt;/a&gt;にある通り、各入力要素の &lt;code&gt;validity&lt;/code&gt; プロパティを参照することで、入力必須項目が空であるかを示す &lt;code&gt;valueMissing&lt;/code&gt; などの真偽値をスクリプト側から判定できます。&lt;/p&gt;
&lt;h3&gt;支援技術へ状態を伝える三つの属性&lt;/h3&gt;
&lt;p&gt;調査において重要なのは、視覚的な変化と同時に、支援技術へ向けた属性がどのように構成されているかを確かめることです。
教材のHTML構造では、要素同士を結び付けるために次の三つの属性が使われています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ラベルの明示&lt;/strong&gt;：&lt;code&gt;label&lt;/code&gt; 要素の &lt;code&gt;for&lt;/code&gt; 属性と &lt;code&gt;input&lt;/code&gt; 要素の &lt;code&gt;id&lt;/code&gt; 属性の値を一致させることで、入力欄とラベルを関連付けます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;補足説明の関連付け&lt;/strong&gt;：&lt;code&gt;aria-describedby&lt;/code&gt; 属性に半角スペース区切りで二つのID（&lt;code&gt;emailHelp formMessage&lt;/code&gt;）を指定し、事前の注記と動的に変化する結果表示領域の両方を入力欄へ結び付けます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不備状態の通知&lt;/strong&gt;：検証に失敗したタイミングでスクリプトが &lt;code&gt;aria-invalid=&quot;true&quot;&lt;/code&gt; を設定し、入力欄が不備を抱えている事実を伝達します。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;有効な形式の入力と通信の非発生&lt;/h3&gt;
&lt;p&gt;続いて、入力欄へ教材で例示されている &lt;code&gt;reader@example.test&lt;/code&gt; という文字列を入力し、再度「入力を確認」ボタンを押します。
すると赤い警告枠は消去され、緑色の枠の中に「入力形式を確認しました。サーバーへの送信はしていません。」という案内文が表示されます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/02e01412393535c6/images/11-form-valid.png&quot; alt=&quot;メール形式の確認後。サーバーへは送信していない&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ここで確認できた事実は、入力された文字列がブラウザーの構文規則を満たしていると判定されたことだけです。
教材は送信イベントの既定動作をpreventDefault()で止め、そのハンドラーにも通信処理を記述していません。preventDefault()だけで任意のJavaScript通信まで禁止されるわけではありません。
画面上の検証は書式上の明らかな間違いを即座に利用者に気付かせるための仕組みであり、実在性や到達性の確認ではないという境界を意識する必要があります。&lt;/p&gt;
&lt;h3&gt;入力値検証の挙動切り分け表&lt;/h3&gt;
&lt;p&gt;空欄と&lt;a href=&quot;mailto:reader@example.test&quot; rel=&quot;noopener noreferrer&quot;&gt;reader@example.test&lt;/a&gt;は実際の画面で確認しました。次の表のreaderとreader@はソースと入力制約からの予想であり、今回の操作記録には含まれません。追試候補として区別してください。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;入力値の条件&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;参照される判定プロパティ&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;code&gt;aria-invalid&lt;/code&gt; の値&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;画面の表示文言&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;フォーカス移動&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;空欄（未入力）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;validity.valueMissing === true&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;true&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;メールアドレスを入力してください。&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;入力欄へ自動移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;reader&lt;/code&gt;（@記号なし）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;validity.typeMismatch === true&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;true&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;メールアドレスの形式を確認してください。&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;入力欄へ自動移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;reader@&lt;/code&gt;（ドメイン欠落）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;validity.typeMismatch === true&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;true&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;メールアドレスの形式を確認してください。&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;入力欄へ自動移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;reader@example.test&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;validity.valid === true&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&quot;false&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;入力形式を確認しました。サーバーへの送信はしていません。&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;フォーカス移動なし&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;値が存在しないことによる違反と、構文が誤っている違反とでは、スクリプトが参照している内部フラグが分かれています。&lt;/p&gt;
&lt;h3&gt;境界値の追加検証案&lt;/h3&gt;
&lt;p&gt;実際の運用で生じる多様な入力に対応するためには、さらに踏み込んだ境界値の振る舞いを点検する必要があります。
追加で検証すべき値として次の四つの観点を提案します。&lt;/p&gt;
&lt;p&gt;第一に、前後に半角空白が含まれた文字列（例：&lt;code&gt;&quot; reader@example.test &quot;&lt;/code&gt;）の入力です。貼り付けた文字列とinput.valueとして取得できる値を比較し、ブラウザーの値の正規化を含めて確かめる課題です。
第二に、全角英数字や全角記号（例：&lt;code&gt;ｒｅａｄｅｒ＠ｅｘａｍｐｌｅ．ｔｅｓｔ&lt;/code&gt;）の入力です。自動的に半角へ正規化するのか、再入力を求めるのかを判断する材料になります。
第三に、実在性が疑わしい特殊なドメイン形式（例：&lt;code&gt;reader@localhost&lt;/code&gt; やドメイン部に記号のみが並ぶ形式）の入力です。フロントエンドの段階で厳密に弾くべきかどうかを検討する手掛かりとなります。
第四に、表示幅を大きく超える長い文字列の入力です。表示領域から文字があふれてレイアウトを損ねないかを確かめる上で有効です。&lt;/p&gt;
&lt;h3&gt;調査手順の整理と改善コードの考察&lt;/h3&gt;
&lt;p&gt;フォームのエラー処理を調査する手順は次の通りです。
まず、送信操作を試みた直後にフォーカスが目的の入力欄へ移動したかを確かめます。
次に、入力欄要素に対して &lt;code&gt;aria-invalid&lt;/code&gt; 属性が適切に付与または解除されているかを点検します。
最後に、表示された警告メッセージのIDが &lt;code&gt;aria-describedby&lt;/code&gt; 属性に含まれており、入力欄の補足情報として認識される状態になっているかを確かめます。&lt;/p&gt;
&lt;p&gt;教材の実装では、不備が見つかった際に対象となる要素を直接指定して &lt;code&gt;email.focus()&lt;/code&gt; を呼び出しています。
複数項目の検証に対応させるための机上の改善提案コードを以下に示します。なお、本コードは設計の考え方を説明するための提案であり、本教材の環境では未実行のコードです。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 未実行の改善提案コード&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; invalidField&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; form.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;:invalid&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (invalidField) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  invalidField.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;focus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;:invalid&lt;/code&gt; 擬似クラスを問い合わせることで、DOMツリーの中で最初に検証に引っかかった入力欄を特定し、利用者の操作位置を先頭のエラー箇所へと導くことができます。&lt;/p&gt;
&lt;h2&gt;9. キーボードで操作の位置を追う&lt;/h2&gt;
&lt;h3&gt;操作の現在地を画面上で見失う現象&lt;/h3&gt;
&lt;p&gt;マウス操作ではカーソルを重ねて要素をクリックしますが、キーボード操作ではフォーカスを示す視覚的な手がかりに頼って操作位置を把握します。Tabキーを押しても枠線が表示されなかったり、操作の前後で表示位置が飛んだりすると現在地を見失います。操作の現在地を正確に追うためには、キーボード入力を通じて画面上で生じる輪郭線の変化と要素の状態を切り離して観察する必要があります。本章ではローカル教材の画面を用い、Tabキーによる移動、輪郭線の可視性、要素の状態変化を観測します。&lt;/p&gt;
&lt;h3&gt;教材のキーボード操作画面とフォーカス表示の確認&lt;/h3&gt;
&lt;p&gt;教材画面の構造を確認します。対象は &lt;code&gt;http://127.0.0.1:9033/demo/?case=focus&lt;/code&gt; で呼び出される練習パネルです。教材はHTMLとJSONの2ファイルをHTTPサーバーで配信して動作します。&lt;/p&gt;
&lt;p&gt;以下は、&lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;button:focus-visible,input:focus-visible,a:focus-visible{outline:4px solid #cc7300;outline-offset:4px}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この教材では、&lt;code&gt;:focus-visible&lt;/code&gt; 疑似クラスに対して太さ &lt;code&gt;4px&lt;/code&gt; のオレンジ色（&lt;code&gt;#cc7300&lt;/code&gt;）の実線輪郭と、要素の外側へ &lt;code&gt;4px&lt;/code&gt; 離す &lt;code&gt;outline-offset&lt;/code&gt; が定義されています。HTML内には「練習操作へ移動」というアンカーリンク、そして「最初の操作」ボタンと「次の操作」ボタンが配置されています。&lt;/p&gt;
&lt;p&gt;次の画像は、最初のボタンをクリックしたあとにTabキーを一度押した際の実観測結果です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-3cb16dec171d477fa5053f323bae0325.r2.dev/articles/chrome-devtools-web-investigation/c193169b80a149d1/images/12-keyboard-focus.png&quot; alt=&quot;Tabで次の操作へ移動したフォーカス。直前の操作結果は残る&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;オレンジ色の輪郭線が「次の操作」ボタンの周囲に表示される一方、ステータスメッセージ欄には「最初の操作を実行しました。」という文字列が残っています。&lt;/p&gt;
&lt;h3&gt;観測結果の切り分けとフォーカスと実行の分離&lt;/h3&gt;
&lt;p&gt;前掲の画像の状態を読み解くには、「現在どの要素が入力の標的になっているか」というフォーカス（focus）と、「その要素の動作が呼び出されたか」というアクティベーション（activation、実行）を分離して評価します。&lt;/p&gt;
&lt;p&gt;実観測の手順では、まず「最初の操作」ボタンをクリックしました。これにより結果通知欄に「最初の操作を実行しました。」と書き込まれました。その直後にTabキーを押下し、フォーカスが次の「次の操作」ボタンへ前進しました。&lt;/p&gt;
&lt;p&gt;Tabキーでフォーカスを「次の操作」へ移動させただけでは、そのボタンのクリック処理は実行されません。結果通知欄の文言が変化していない事実は、フォーカスの移動とアクションの実行が別々の事象である証拠です。公式ガイドの &lt;a href=&quot;https://developer.chrome.com/docs/devtools/accessibility/focus&quot; rel=&quot;noopener noreferrer&quot;&gt;Track element focus&lt;/a&gt; でも、現在フォーカスを持つ要素を特定する手法として &lt;code&gt;document.activeElement&lt;/code&gt; の参照が示されています。この教材では、現在のフォーカス位置と実行済みの操作結果を別々に確認します。&lt;/p&gt;
&lt;h3&gt;フォーカス移動と操作実行の整理&lt;/h3&gt;
&lt;p&gt;押下したキーが「フォーカスの移動」を担うのか、「要素の実行」を担うのかを分類します。以下の表は標準的な要素で期待する挙動を整理した追試用の表です。各キーを今回すべて試した結果ではありません。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;キー操作&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;挙動の種別&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;リンク（&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;）での動作&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;ボタン（&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;）での動作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Tab&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;フォーカス移動&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;次のフォーカス可能要素へ進む&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;次のフォーカス可能要素へ進む&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Shift + Tab&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;フォーカス移動&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;前のフォーカス可能要素へ戻る&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;前のフォーカス可能要素へ戻る&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Enter&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;操作の実行（activation）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;リンク先へ遷移（教材ではページ内移動）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ボタンのクリック処理を実行する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;操作の実行（activation）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;通常はページスクロール（リンクは未実行）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ボタンのクリック処理を実行する&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;TabキーおよびShift+Tabキーの役割はフォーカス位置の変更にとどまります。一方、実行キーの挙動はHTML要素によって異なります。ボタン要素はEnterキーとSpaceキーの双方がクリックイベントを誘発しますが、標準的なリンク要素ではEnterキーのみが遷移を実行し、Spaceキーはスクロールとして処理されます。&lt;/p&gt;
&lt;h3&gt;巡回順序と逆方向移動の追試手順&lt;/h3&gt;
&lt;p&gt;教材を対象にした具体的な調査手順を組み立てます。&lt;/p&gt;
&lt;p&gt;まずアドレスバーやページ最上部から始め、マウスを使わずにTabキーを単独で押下していきます。ナビゲーションリンク群を通過し、メインコンテンツ内の「練習操作へ移動」リンク、「最初の操作」ボタン、「次の操作」ボタンへとフォーカスが順に移動することを目視で確認します。HTMLソースのDOMの並び順とフォーカス移動順が一致しているかを照合します。CSSプロパティで視覚的な並び順だけを入れ替えている場合、フォーカス移動が画面上で不規則に跳躍する現象が発生します。&lt;/p&gt;
&lt;p&gt;次に逆方向の移動を確認します。「次の操作」ボタンに輪郭線がある状態でShift+Tabキーを押します。フォーカスが直前の「最初の操作」ボタンへ巻き戻り、後退経路が確保されているかを確認します。&lt;/p&gt;
&lt;h3&gt;リンクとボタンの実行追試案&lt;/h3&gt;
&lt;p&gt;各要素の実行（activation）に関する追試を行います。「最初の操作」にフォーカスがある状態でSpaceキーを押し、結果通知欄が「最初の操作を実行しました。」に更新されるかを確認します。続いてTabキーで「次の操作」へ移り、Enterキーを押して結果通知欄が「次の操作を実行しました。」へ書き換わるかを確認します。&lt;/p&gt;
&lt;p&gt;次にアンカーリンクを検証します。Shift+Tabキーで「練習操作へ移動」リンクへ戻し、SpaceキーとEnterキーの挙動を比較します。標準ブラウザ仕様ではSpaceキーでリンク遷移は発生せず、Enterキーでターゲット要素 &lt;code&gt;#practice&lt;/code&gt; へのジャンプが発生します。&lt;/p&gt;
&lt;h3&gt;スクリプトによる動的フォーカス制御の検証案&lt;/h3&gt;
&lt;p&gt;JavaScriptからプログラム的にフォーカスを移動させる場合の変更案を検討します。以下は机上の提案コードであり、本章の執筆時点では未実行の例です。実観測の記録とは区別して扱います。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 提案コード（机上の例・未実行）：操作完了後にフォーカスを結果領域へ移す試み&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; practiceSection&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getElementById&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;practice&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; resultArea&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getElementById&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;focusResult&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;practiceSection.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;click&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (event.target.tagName &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;BUTTON&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    resultArea.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setAttribute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;tabindex&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;-1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    resultArea.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;focus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この提案コードでは、ボタンがクリックされた直後に結果表示領域（&lt;code&gt;&amp;lt;p id=&quot;focusResult&quot;&amp;gt;&lt;/code&gt;）へフォーカスを移動させます。非対話的要素に &lt;code&gt;tabindex=&quot;-1&quot;&lt;/code&gt; を付与することで、スクリプトからの &lt;code&gt;.focus()&lt;/code&gt; 呼び出しが可能になります。この教材のボタン操作においてメッセージへ注意を向ける手段になりますが、キーボードで連続操作を行う際にフォーカスを戻す手間が生じる点にも配慮が必要です。&lt;/p&gt;
&lt;h3&gt;今回の調査範囲&lt;/h3&gt;
&lt;p&gt;本章の実観測は、macOS上のChrome環境において教材ページの画面表示とキーボード入力を突き合わせた範囲に限られます。実施したのは「最初の操作」のクリック後、Tabを一度押して「次の操作」へ移動し、輪郭線とメッセージを確認する操作です。Shift+Tab、Enter、Spaceおよびリンクへのキー操作は追試案であり、今回の記録には含みません。&lt;/p&gt;
&lt;p&gt;スクリーンリーダーによる音声読み上げ、Windows環境や他社製ブラウザでの検証、モバイル実機検証、パフォーマンストレースなどは本章の作業範囲には含まれていません。画面上に輪郭線が見えていることと、支援技術へ名前や役割が正しく伝達されているかは別の検証事項となります。&lt;/p&gt;
&lt;h2&gt;10. 見えるラベルとアクセシビリティ情報を照合する&lt;/h2&gt;
&lt;h3&gt;画面の文字と支援技術へ渡る情報の食い違い&lt;/h3&gt;
&lt;p&gt;画面上で見えている文字列と支援技術に伝達される情報は必ずしも一致しません。ボタンや入力欄に直感的な文字が表示されていても、支援技術が参照する内部情報が欠落していたり、異なる内容が設定されていたりすることがあります。&lt;/p&gt;
&lt;p&gt;ブラウザーはHTMLを解析してDOMツリーを構築すると同時に、支援技術にUI情報を中継する&lt;strong&gt;アクセシビリティツリー&lt;/strong&gt;を生成します。ここでは画面上の要素ごとに&lt;strong&gt;名前&lt;/strong&gt;（Name）、&lt;strong&gt;役割&lt;/strong&gt;（Role）、&lt;strong&gt;状態&lt;/strong&gt;（State）が整理されます。&lt;/p&gt;
&lt;p&gt;Chrome DevToolsのElementsパネルにあるAccessibilityペインでは、算出された属性を確認できます。公式解説の&lt;a href=&quot;https://developer.chrome.com/docs/devtools/accessibility/reference&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevTools Accessibility Reference&lt;/a&gt;にある通り、ツリーやプロパティの調査が可能です。画面表示と算出値を突き合わせることで、ラベルの上書きや状態管理の不備を切り分けられます。&lt;/p&gt;
&lt;h3&gt;入力欄と開閉操作から組み立てる調査設計&lt;/h3&gt;
&lt;p&gt;この章は教材ソースを材料にした追試の設計です。Accessibilityペインのプロパティ表示や実際の音声読み上げを今回確認した記録ではありません。&lt;/p&gt;
&lt;p&gt;今回の照合演習では、教材サーバーを起動し、ブラウザーで &lt;code&gt;http://127.0.0.1:9033/demo/?case=form&lt;/code&gt; や &lt;code&gt;http://127.0.0.1:9033/demo/?case=dom&lt;/code&gt; を開いて対象画面を確認します。教材はHTMLとJSONの2ファイルを配信する構成です。&lt;/p&gt;
&lt;p&gt;まず、&lt;code&gt;demo/index.html&lt;/code&gt; の入力フォーム周辺のマークアップです。&lt;/p&gt;
&lt;p&gt;フォームの章で掲載したform、label、inputと結果表示を使って追試します。&lt;/p&gt;
&lt;p&gt;調査では、&lt;code&gt;input&lt;/code&gt; の名前が &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; のテキストから計算されているか、&lt;code&gt;placeholder&lt;/code&gt; が誤って名前に採用されていないか、&lt;code&gt;#emailHelp&lt;/code&gt; が &lt;code&gt;aria-describedby&lt;/code&gt; 経由で算出されているかを確認します。未入力送信時にスクリプトで &lt;code&gt;aria-invalid&lt;/code&gt; が付与されるかも対象とします。&lt;/p&gt;
&lt;p&gt;トグルボタンの初期実装は次の通りです。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;toggle&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-expanded&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-controls&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;detail&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;詳細を表示&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;detail&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;役割が「button」、名前が「詳細を表示」となっているかを確認します。クリック直後に &lt;code&gt;aria-expanded&lt;/code&gt; が「false」から「true」へ反転し、ボタン自身のテキストが「詳細を閉じる」に切り替わるかという変化を追跡します。&lt;/p&gt;
&lt;h3&gt;名前とプレースホルダーの計算順序を切り分ける&lt;/h3&gt;
&lt;p&gt;テキストボックス内の薄い文字列は形式の例を示す補助情報であり、入力欄の永続的な名前ではありません。&lt;/p&gt;
&lt;p&gt;教材のフォームでは、&lt;code&gt;&amp;lt;label for=&quot;email&quot;&amp;gt;&lt;/code&gt; により「連絡先メールアドレス（教材用）」が提供され、&lt;code&gt;input&lt;/code&gt; には &lt;code&gt;placeholder=&quot;reader@example.test&quot;&lt;/code&gt; が指定されています。Accessibilityペインでこの要素を選択すると、&lt;strong&gt;算出された名前&lt;/strong&gt;（Computed Name）の算出元として &lt;code&gt;label&lt;/code&gt; 要素が優先され、&lt;code&gt;placeholder&lt;/code&gt; は補助的な位置づけであることが確認できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; との紐付けが欠落している場合、ブラウザーはフォールバックとして &lt;code&gt;placeholder&lt;/code&gt; を名前に採用することがあります。照合作業では名前の値だけでなく、どの要素や属性から導き出されたのかという計算経路まで確認します。&lt;/p&gt;
&lt;p&gt;あわせて &lt;code&gt;aria-describedby=&quot;emailHelp formMessage&quot;&lt;/code&gt; による補足情報も確認します。計算されたプロパティ一覧において、説明（Description）に「架空のアドレスで練習できます。入力は外部に送信しません。」および「未確認です。」が連結されているかを記録します。&lt;/p&gt;
&lt;h3&gt;操作に応じた状態の変化を追跡する&lt;/h3&gt;
&lt;p&gt;フォームとDOMの章で掲載したイベントハンドラーを確認し、次の項目を追試します。&lt;/p&gt;
&lt;p&gt;フォーム送信処理では、入力不正時に &lt;code&gt;email.setAttribute(&apos;aria-invalid&apos;, &apos;true&apos;)&lt;/code&gt; が実行され、&lt;code&gt;#formMessage&lt;/code&gt; がエラー文言へ書き換わります。空欄送信時に &lt;code&gt;aria-invalid&lt;/code&gt; が「true」へ変化し、フォーカスが &lt;code&gt;#email&lt;/code&gt; へ移動するかを観察します。&lt;/p&gt;
&lt;p&gt;トグルボタンでは、クリックにより &lt;code&gt;aria-expanded&lt;/code&gt; が「false」から「true」へ更新され、テキストが「詳細を閉じる」に変化します。この教材のボタン操作では、クリックによって属性とテキストが更新されます。&lt;/p&gt;
&lt;p&gt;なお、&lt;code&gt;role=&quot;status&quot;&lt;/code&gt; によるライブ領域の指定は、属性がDOMに存在し計算されたプロパティに反映されていることの確認にとどめます。実際の音声読み上げの出力可否はOSや支援技術の環境に依存するため、ブラウザー単体の観察から読み上げを断定することはできません。&lt;/p&gt;
&lt;h3&gt;装飾用絵文字と無関係な情報の遮断&lt;/h3&gt;
&lt;p&gt;装飾要素がアクセシビリティツリーに混入するとノイズになります。純粋な装飾要素はツリーから除外します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- demo/index.html の抜粋 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;art&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-hidden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;☕&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;朝のコーヒー&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;長い説明文でも内容を読めるカードを作ります。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;コーヒーカップの絵文字「☕」を囲む &lt;code&gt;div&lt;/code&gt; には &lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt; が指定されています。Accessibilityペインで検証すると除外（Ignored）と表示され、支援技術向けの情報から遮断されていることが裏付けられます。属性が欠落していると絵文字がテキストとして登録され、見出しと重複する冗長な案内を生みます。&lt;/p&gt;
&lt;h3&gt;自動チェックの範囲と手動確認の境界&lt;/h3&gt;
&lt;p&gt;自動チェックと手動確認の守備範囲を整理します。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;検査対象&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;自動チェックの範囲&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;人の手による照合範囲&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;確認項目&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;入力欄の名前&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;for&lt;/code&gt; と &lt;code&gt;id&lt;/code&gt; の紐付け有無&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;表示文言とラベルの一致&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Computed Nameと算出元要素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;プレースホルダー&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;属性値の構文チェック&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ラベル代用になっていないか&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;名前に誤採用されていないか&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;トグルボタンの状態&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;aria-expanded&lt;/code&gt; の属性妥当性&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;操作後の状態と値の整合性&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;操作前後の属性変化とDOM更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;エラー通知&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;aria-invalid&lt;/code&gt; 等の属性形式&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;警告文と入力欄の関連性&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;バリデーション時の属性とフォーカス&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;装飾絵文字&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;属性の有無&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;隠蔽すべき装飾かの判断&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;AccessibilityペインでのIgnored状態&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;構文チェックはツールに委ねつつ、文脈上の妥当性や状態変化はペインで直接確かめます。&lt;/p&gt;
&lt;h3&gt;観測事実を分離した修正報告の作成&lt;/h3&gt;
&lt;p&gt;調査結果を報告する際は、以下の机上の例のように観測事実と提案を分離して記述します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- 報告記述の机上の例 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 対象要素: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`#toggle`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 観測された状態: ボタンテキスト「詳細を表示」に対し、属性が aria-expanded=&quot;true&quot; のまま不一致&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 期待される挙動: 展開状態と属性値が連動していること&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;修正案では、表示と属性のどちらが現在の状態を誤って表しているかを先に調べます。属性だけを一律に書き換えると表示との不整合が残るため、状態を更新するハンドラーとの対応を確認してください。&lt;/p&gt;
&lt;h2&gt;11. Consoleの情報から次の確認を選ぶ&lt;/h2&gt;
&lt;p&gt;Webページで不具合が起きたとき、最初にConsoleパネルを開く場面は多くあります。しかし、Consoleに記録された情報と画面上の症状の間には、時間的なずれや処理の食い違いが生じます。出力された記録の性質を慎重に腑分けし、次の確認手順への手がかりとして扱う必要があります。&lt;/p&gt;
&lt;h3&gt;エラーの発生時刻と画面の症状を結び付ける&lt;/h3&gt;
&lt;p&gt;Consoleのメッセージを追う際は、操作と時刻の前後関係を整理します。操作直前の表示状態を起点とし、クリックやキー入力を行った瞬間に何行の記録がConsoleへ追加されたかを観察します。操作と同時にスタックトレースを伴う例外が表示されたのであれば、その操作に対応するイベントハンドラーの実行中に問題が生じたと推測できます。一方で、操作より前に出ていたログは別の要因による副次的な例外の可能性があります。&lt;/p&gt;
&lt;p&gt;公式の解説である&lt;a href=&quot;https://developer.chrome.com/docs/devtools/console/reference&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevTools Console Reference&lt;/a&gt;が示すとおり、DevToolsのConsoleは重要度に応じたログのフィルタリングや、例外が発生した経路をたどるスタックトレースの展開機能を提供しています。ログレベル（Verbose、Info、Warnings、Errors）の選択状態によって出力が隠されていないかもあわせて確認します。&lt;/p&gt;
&lt;h3&gt;ログが出力されない原因をコードの構造から読む&lt;/h3&gt;
&lt;p&gt;JavaScriptが例外をcatchした後にログを出さない構造では、捕捉した例外の内容がConsoleに残らないことがあります。教材はHTMLとJSONの2ファイルをHTTPサーバー（&lt;code&gt;http://127.0.0.1:9033/demo/?case=network&lt;/code&gt;）で配信して通信を行います。以下のJavaScriptコードを確認してみます。&lt;/p&gt;
&lt;p&gt;これは &lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;p&gt;対応する教材ハンドラーは前の実習章で掲載しています。ここでは処理の分岐とログ出力の有無を読み直します。&lt;/p&gt;
&lt;p&gt;この処理において、失敗ボタンを押して &lt;code&gt;missing-product.json&lt;/code&gt; を要求したとき、HTTPの応答は404になります。しかしコード側では &lt;code&gt;if(!response.ok)&lt;/code&gt; によって分岐し、画面内の &lt;code&gt;#networkResult&lt;/code&gt; 要素のテキストを書き換えて &lt;code&gt;return&lt;/code&gt; しています。HTTPステータスコードが404であっても &lt;code&gt;fetch&lt;/code&gt; のPromise自体は正常に解決されるため、プログラムの例外としては扱われません。アプリがconsole.errorを呼んでいなくても、ブラウザー自身が404などのネットワークエラーをConsoleに表示する場合があります。HTTPのエラー表示と、アプリが投げた例外を区別してください。&lt;/p&gt;
&lt;p&gt;また、通信自体が失敗して &lt;code&gt;catch&lt;/code&gt; 節が実行された場合も、画面に案内文を代入するのみでエラーオブジェクトを出力していません。このcatch自身はConsoleへ情報を出しません。ただし、ブラウザーによる通信エラーの表示まで抑止するわけではありません。&lt;/p&gt;
&lt;h3&gt;アプリケーションのコードと拡張機能のノイズを区別する&lt;/h3&gt;
&lt;p&gt;Consoleのエラーには、ブラウザー拡張機能がページ内に注入したスクリプト由来のものが混入することがあります。区別の基準となるのは、スタックトレースの右端に表示されているスクリプトの送信元やURLです。&lt;code&gt;chrome-extension://&lt;/code&gt; で始まるスクリプトは拡張機能由来だと判断する手掛かりになります。ハッシュ付きのファイル名だけでは、拡張機能かアプリかは判断できません。調査対象のオリジンを指す記録についても、呼び出し元のコードや関連する要求を照合します。拡張機能の動かない条件と比較するときは、シークレットモードでも実行を許可された拡張機能がないかを確認し、ログイン状態やキャッシュ条件の違いも記録します。&lt;/p&gt;
&lt;h3&gt;Consoleの調査手順と次の確認の対応&lt;/h3&gt;
&lt;p&gt;Consoleの観測結果から、次にどの機能やコードへ進むべきかを整理した表が以下です。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Consoleの観測結果&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;推定される状況&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;次に確認する場所や操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;赤色エラー（404 Not Found）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;要求したリソースがサーバー側に存在しない&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;NetworkパネルでリクエストURLとパス指定を照合する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;赤色エラー（TypeError等）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;nullやundefinedに対するプロパティ参照などの可能性&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Sourcesパネルでスタックトレースの該当行を開き、変数の内容を確認する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;エラーなし（画面表示のみ失敗）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;response.ok&lt;/code&gt; による条件分岐や &lt;code&gt;catch&lt;/code&gt; で握りつぶし&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;対象ハンドラーのJavaScriptコードを読み、分岐条件を追跡する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;エラーなし（画面変化もなし）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;イベントリスナーの未登録、またはセレクターの不一致&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Elementsパネルで要素のイベント設定やid属性の綴りを照合する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;chrome-extension://&lt;/code&gt; からの警告&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ブラウザー拡張機能による干渉&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;シークレットウィンドウで再操作し、再現性を確認する&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Consoleの役割は原因の最終決定ではなく、次に向かうべきパネルを決定するための手がかりです。&lt;/p&gt;
&lt;h3&gt;Console操作を用いた追試案とDOMの安全な確認&lt;/h3&gt;
&lt;p&gt;教材のDOM切り替え処理（&lt;code&gt;http://127.0.0.1:9033/demo/?case=dom&lt;/code&gt;）において、ボタンを押しても詳細要素が期待通りに展開されない不具合を想定します。教材の該当箇所では次のコードが動いています。&lt;/p&gt;
&lt;p&gt;これは &lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;p&gt;対応する教材ハンドラーは前の実習章で掲載しています。ここでは処理の分岐とログ出力の有無を読み直します。&lt;/p&gt;
&lt;p&gt;この処理の状態を確認する際、ページの状態を変更せずに読み取る追試案として、以下のスクリプトが考えられます。&lt;/p&gt;
&lt;p&gt;提案コード（机上の例・未実行）：&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ボタン要素の属性と関連先要素の現在の状態を非破壊で取得する案&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  expanded: document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#toggle&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)?.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getAttribute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;aria-expanded&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  childCount: document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#detail&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)?.childElementCount,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  detailText: document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#detail&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)?.textContent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この提案コードは机上の例であり、実際の観測記録とは異なります。値の書き換えを行わず読み取り専用プロパティに限定して確認することで、調査操作による状態破壊を防ぎます。本教材のボタン操作の挙動確認においても、属性の直接書き換えではなくボタンクリック操作によって状態を遷移させることが確実です。&lt;/p&gt;
&lt;h3&gt;入力内容の検証処理におけるConsoleの位置付け&lt;/h3&gt;
&lt;p&gt;教材のフォーム処理（&lt;code&gt;http://127.0.0.1:9033/demo/?case=form&lt;/code&gt;）では、HTML5標準の &lt;code&gt;validity&lt;/code&gt; プロパティを参照して分岐を行っています。&lt;/p&gt;
&lt;p&gt;これは &lt;code&gt;demo/index.html&lt;/code&gt; の抜粋です。&lt;/p&gt;
&lt;p&gt;対応する教材ハンドラーは前の実習章で掲載しています。ここでは処理の分岐とログ出力の有無を読み直します。&lt;/p&gt;
&lt;p&gt;ユーザーが空欄のまま送信ボタンを押した際、&lt;code&gt;event.preventDefault()&lt;/code&gt; によってブラウザー本来の送信を止め、独自の文言を &lt;code&gt;#formMessage&lt;/code&gt; に表示します。このハンドラーはエラー文をConsoleへ出力していません。実際のConsoleに何もないかは別途確認する必要があります。画面上にメッセージが出ているにもかかわらずConsoleが無反応である状況は、このようなガード節が機能した結果です。&lt;/p&gt;
&lt;h3&gt;Consoleの情報を過信せず次の段階へ進む考え方&lt;/h3&gt;
&lt;p&gt;Consoleに表示された文字列は、実行環境が処理を継続できなくなった地点や、明示的に出力させた地点を指しています。真の原因がその手前の代入ミスや呼び出し元からの引数にある場合、エラー箇所だけの修正では根本的な解決になりません。&lt;/p&gt;
&lt;p&gt;発生時刻の照合、拡張機能ノイズの選別、コードによる例外捕捉の有無を確認した後は、Sourcesパネルでのブレークポイント設定やステップ実行へ進み、変数値の遷移を追跡します。&lt;/p&gt;
&lt;h2&gt;12. 処理を止めて状態の変化を調べる&lt;/h2&gt;
&lt;p&gt;画面の表示が変化した後からでは、JavaScriptの処理順序や途中で変数がどう入れ替わったかを正確に復元できない場面があります。処理を途中で一時停止させ、その瞬間に保持されている変数の値やDOMの状態を直接観察する手法がブレークポイントです。&lt;/p&gt;
&lt;p&gt;公式ドキュメントの&lt;a href=&quot;https://developer.chrome.com/docs/devtools/javascript/breakpoints&quot; rel=&quot;noopener noreferrer&quot;&gt;Pause code with breakpoints&lt;/a&gt;で解説されているように、Chrome DevToolsのSourcesパネルではコード行への配置やイベント発生に合わせた停止が可能です。本章ではSourcesパネルを活用し、内部状態の変化を切り分ける手順を確認します。&lt;/p&gt;
&lt;p&gt;この章は未実施の発展演習です。以下の停止時の値は教材ソースから予想した値であり、Sourcesパネルで取得した実測ログではありません。&lt;/p&gt;
&lt;h3&gt;停止箇所と変数の観測計画をあらかじめ決める&lt;/h3&gt;
&lt;p&gt;実行を止める前に、どの行で止め、スコープ内のどの変数に何の値が入ることを期待するか仮説を立てておきます。変化が起きる直前の値と直後の値を比較できる位置を選びます。教材のJavaScriptは行を詰めているため、行番号だけでは複数文のどこで止まるかを特定できません。停止位置の強調表示と変数の有効範囲を確認します。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;調査対象の機能&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;設定する停止箇所&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;停止時に確認する対象と期待値&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;停止直後に変化する状態&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;詳細の開閉（DOM）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;const open = button.getAttribute(...)&lt;/code&gt; の直後&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;変数 &lt;code&gt;open&lt;/code&gt; が論理値（trueまたはfalse）であること&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;aria-expanded&lt;/code&gt; 属性とボタンの表示テキスト&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;詳細の開閉（DOM）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;detail.replaceChildren()&lt;/code&gt; の実行直前&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;要素 &lt;code&gt;#detail&lt;/code&gt; の子要素数が0または既存要素であること&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;#detail&lt;/code&gt; 内への &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; 要素の追加または消去&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;データ通信（Network）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;if (!response.ok)&lt;/code&gt; の判定行&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;response.status&lt;/code&gt; が200番台か400番台か、&lt;code&gt;response.ok&lt;/code&gt; の真偽&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;成功用表示かエラー用表示クラス &lt;code&gt;bad&lt;/code&gt; の付与&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;フォーム検証（Form）&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;const valid = email.validity.valid&lt;/code&gt; の直後&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;email.value&lt;/code&gt; の入力文字列と &lt;code&gt;validity&lt;/code&gt; オブジェクトの各フラグ&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;aria-invalid&lt;/code&gt; 属性値とエラーメッセージの出力内容&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;停止させた瞬間の変数確認を整理しておくことで、何が期待通りでどこから想定が崩れたのかを即座に判定できます。&lt;/p&gt;
&lt;h3&gt;開閉処理におけるDOM操作前後の状態比較&lt;/h3&gt;
&lt;p&gt;教材の「DOM」画面（&lt;code&gt;http://127.0.0.1:9033/demo/?case=dom&lt;/code&gt;）では、ボタンを押すことで詳細説明が開閉します。コードは &lt;code&gt;demo/index.html&lt;/code&gt; からの抜粋です。&lt;/p&gt;
&lt;p&gt;DOMの章で掲載したtoggleのクリックハンドラーを調べます。&lt;/p&gt;
&lt;p&gt;ボタンのクリック直後に停止させ、変数 &lt;code&gt;open&lt;/code&gt; の値が決まる瞬間を確認します。初期状態のHTMLでは &lt;code&gt;aria-expanded=&quot;false&quot;&lt;/code&gt; のため、&lt;code&gt;button.getAttribute(&apos;aria-expanded&apos;) === &apos;true&apos;&lt;/code&gt; の評価結果として変数 &lt;code&gt;open&lt;/code&gt; には &lt;code&gt;false&lt;/code&gt; が代入されます。Scopeペイン上で &lt;code&gt;button&lt;/code&gt; と &lt;code&gt;open: false&lt;/code&gt; が確認できます。&lt;/p&gt;
&lt;p&gt;次の文へ進めて &lt;code&gt;button.setAttribute(&apos;aria-expanded&apos;, String(!open))&lt;/code&gt; を通過させると、ElementsパネルのDOMツリー上で &lt;code&gt;aria-expanded&lt;/code&gt; が &lt;code&gt;&quot;true&quot;&lt;/code&gt; に更新されます。&lt;code&gt;detail.replaceChildren()&lt;/code&gt; の通過時点では &lt;code&gt;#detail&lt;/code&gt; 領域は空です。&lt;code&gt;if (!open)&lt;/code&gt; ブロック内の &lt;code&gt;detail.append(p)&lt;/code&gt; が実行された瞬間にDOMへ段落要素が挿入されます。実際に画面へ描かれるタイミングは、DOMを更新した瞬間とは区別してください。&lt;/p&gt;
&lt;h3&gt;通信処理の分岐点における真偽値とレスポンスの捕捉&lt;/h3&gt;
&lt;p&gt;「通信」画面（&lt;code&gt;http://127.0.0.1:9033/demo/?case=network&lt;/code&gt;）における非同期通信の分岐を検証します。&lt;/p&gt;
&lt;p&gt;通信とフォームのハンドラーは前の章で掲載した教材コードを参照してください。&lt;/p&gt;
&lt;p&gt;非同期の &lt;code&gt;fetch&lt;/code&gt; を伴う処理では、応答のヘッダーを受け取ってResponseが返った後の &lt;code&gt;if (!response.ok)&lt;/code&gt; の行にブレークポイントを配置します。&lt;/p&gt;
&lt;p&gt;「商品データを取得」ボタンを押した場合、停止時の &lt;code&gt;response&lt;/code&gt; は &lt;code&gt;status: 200&lt;/code&gt; かつ &lt;code&gt;ok: true&lt;/code&gt; です。条件式 &lt;code&gt;!response.ok&lt;/code&gt; は偽となり、&lt;code&gt;await response.json()&lt;/code&gt; へ進みます。「存在しないデータを取得」ボタンを押した場合は &lt;code&gt;response.status&lt;/code&gt; が &lt;code&gt;404&lt;/code&gt;、&lt;code&gt;ok: false&lt;/code&gt; となります。条件式が真となってエラー表示と &lt;code&gt;bad&lt;/code&gt; クラス付与が行われ、&lt;code&gt;return&lt;/code&gt; で後続の処理が中断されます。&lt;/p&gt;
&lt;h3&gt;フォーム検証時のvalidityプロパティと属性の連動&lt;/h3&gt;
&lt;p&gt;「フォーム」画面（&lt;code&gt;http://127.0.0.1:9033/demo/?case=form&lt;/code&gt;）では、送信イベント時に入力値の妥当性をブラウザの標準APIで判定しています。&lt;/p&gt;
&lt;p&gt;通信とフォームのハンドラーは前の章で掲載した教材コードを参照してください。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;const valid = email.validity.valid&lt;/code&gt; の直後にブレークポイントを置きます。空のまま送信して停止させた場合、Scopeペインの &lt;code&gt;email.validity&lt;/code&gt; で &lt;code&gt;valid: false&lt;/code&gt; および &lt;code&gt;valueMissing: true&lt;/code&gt; が確認できます。&lt;/p&gt;
&lt;p&gt;行を進めると要素に &lt;code&gt;aria-invalid=&quot;true&quot;&lt;/code&gt; が設定され、三項演算子により「メールアドレスを入力してください。」が出力されます。なお、入力欄に &lt;code&gt;abc&lt;/code&gt; と入力して送信した場合は &lt;code&gt;valueMissing: false&lt;/code&gt; かつ &lt;code&gt;typeMismatch: true&lt;/code&gt; となり、「メールアドレスの形式を確認してください。」に切り替わります（これは机上の動作例です）。&lt;/p&gt;
&lt;h3&gt;停止処理がタイマーやイベント順序に与える影響&lt;/h3&gt;
&lt;p&gt;処理を一時停止させる性質上、時間経過に依存する挙動には副作用が生じます。変数を観察している間も時間は経過しています。&lt;/p&gt;
&lt;p&gt;画面内に &lt;code&gt;setTimeout&lt;/code&gt; や &lt;code&gt;setInterval&lt;/code&gt; が存在する場合、停止と再開によって、通常時とはタイマーや通信完了を処理するタイミングが変わる可能性があります。通信のタイムアウトを設定している処理では、待機時間を超過して再開時にタイムアウト例外が発生することもあります。&lt;/p&gt;
&lt;p&gt;停止中にDevToolsへフォーカスを移す操作自体が、調査対象の入力状態へ影響する場合もあります。&lt;/p&gt;
&lt;h3&gt;複数クリックによる競合と排他制御の検討&lt;/h3&gt;
&lt;p&gt;教材の「通信」画面でボタンを素早く連続クリックした場合、最初の &lt;code&gt;fetch&lt;/code&gt; の完了前に2回目が開始されます。環境によって2回目の応答が先に解決し、遅れて1回目のレスポンスが到着すると画面が上書きされる競合が生じます。&lt;/p&gt;
&lt;p&gt;対応案の一つは、取得中フラグと対象ボタンの非活性化で後続の開始を抑える方法です。今回、連続クリックの競合は実測していません。以下は改善案の実装例です（本章の解説用であり未実行のコードです）。&lt;/p&gt;
&lt;p&gt;この案では、開始時に取得中フラグを立て、成功時も失敗時もfinallyで解除します。対象の二つの取得ボタンだけを無効化し、完了後に元の状態へ戻す設計を考えます。中断した要求や、画面を離れた後に完了した要求の扱いは別の課題です。&lt;/p&gt;
&lt;p&gt;この提案を試す場合は、既存ボタンのハンドラーを新しい制御へ接続し直し、対象ボタンの選び方も確認します。関数を追加するだけでは既存のload呼び出しは変わりません。この処理でも、&lt;code&gt;fetch&lt;/code&gt; 直前にブレークポイントを置いてボタンの &lt;code&gt;disabled&lt;/code&gt; 属性や &lt;code&gt;isLoading&lt;/code&gt; フラグが &lt;code&gt;true&lt;/code&gt; を維持しているかを確認できます。&lt;/p&gt;
&lt;h2&gt;13. 再現できる調査報告へまとめる&lt;/h2&gt;
&lt;p&gt;ブラウザー上で問題の原因らしき箇所を突き止めたら、作業者の手元で起きた事象を別の開発者が同じ順序で追試できる&lt;strong&gt;調査報告票&lt;/strong&gt;としてまとめます。
調査報告には、観測時の前提条件、再現手順、得られた事実、立てた仮説、試行した変更、そして未確認の境界を記述します。&lt;/p&gt;
&lt;h3&gt;調査環境と初期状態の確定&lt;/h3&gt;
&lt;p&gt;再現性の高い報告を作成するため、実行環境の境界を客観的な数値として記録します。OSの種別、ブラウザーの正確なバージョン番号、表示確認時のビューポート幅を明記します。&lt;/p&gt;
&lt;p&gt;今回の検証環境は、macOS上で動作するGoogle Chromeです。細かなブラウザーの版番号とCSSピクセル単位のビューポート寸法は記録していません。画像ファイルの寸法はevidence.jsonに記録していますが、表示条件全体を表す値ではありません。画面は手元のローカル環境でHTTPサーバーを起動し、HTMLとJSONの2ファイルを配信して表示しています。
次のコードは、調査対象となった教材画面の骨格を定義している&lt;code&gt;demo/index.html&lt;/code&gt;からの抜粋です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;frame&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;art&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; aria-hidden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;☕&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;朝のコーヒー&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;長い説明文でも内容を読めるカードを作ります。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;layoutStatus&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;カード：width: 520px。親の内容領域を超えています。&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;親要素&lt;code&gt;&amp;lt;div class=&quot;frame&quot;&amp;gt;&lt;/code&gt;の内側に、初期状態で&lt;code&gt;fixed&lt;/code&gt;クラスを持つ&lt;code&gt;&amp;lt;div id=&quot;product&quot;&amp;gt;&lt;/code&gt;が配置されています。親要素には境界を示す点線枠が設定され、幅はスタイルシートで制限されています。&lt;/p&gt;
&lt;h3&gt;固定幅カードのはみ出しを追試する手順&lt;/h3&gt;
&lt;p&gt;事象を第三者が再現できるよう、作業者が行った操作を時系列の再現手順として記述します。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ローカルサーバーを起動し、ブラウザーで &lt;code&gt;http://127.0.0.1:9033/demo/index.html?case=responsive&lt;/code&gt; にアクセスします。&lt;/li&gt;
&lt;li&gt;画面内に表示される茶色の点線枠で囲まれたカード要素を確認します。&lt;/li&gt;
&lt;li&gt;カード内の文章の右側が表示領域の境界で切れていることを確認します。&lt;/li&gt;
&lt;li&gt;画面上に配置された「親の幅に収める」ボタンをクリックし、カードの横幅が枠内に収まる変化を確認します。&lt;/li&gt;
&lt;li&gt;追加の追試として「固定幅に戻す」をクリックし、元の表示へ戻るか確認します。この戻す操作は今回の撮影記録には含みません。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;観測事実と構造分析&lt;/h3&gt;
&lt;p&gt;再現手順を実行したあと、画面上で起きた事象を客観的な事実として記録します。&lt;/p&gt;
&lt;p&gt;初期状態では、茶色の点線で示された幅360pxのコンテナ枠に対して、商品カード要素が右側へはみ出していました。状態表示欄には「カード：width: 520px。親の内容領域を超えています。」と出力されていました。見出し「朝のコーヒー」に続く説明文の一部が、右側の表示領域から見えなくなっていました。文章自体はカード内でも折り返しており、単純に改行がなかったわけではありません。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;demo/index.html&lt;/code&gt;内で定義されている関連スタイルは次の通りです。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.frame{width:360px;max-width:100%;border:2px dashed #9b6848;padding:12px;overflow:auto}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.fixed{width:520px;background:#e3eff3;padding:20px}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.responsive{width:100%;max-width:520px;background:#e3eff3;padding:20px}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;親要素&lt;code&gt;.frame&lt;/code&gt;の横幅が360pxに制限されているのに対し、子要素&lt;code&gt;.fixed&lt;/code&gt;には&lt;code&gt;width: 520px&lt;/code&gt;が与えられています。親要素の内容領域よりも子要素の指定幅が大きいため、子要素は親の境界を越えてあふれ出します。親の幅360pxと子の固定幅520pxの不一致を報告書に明記します。&lt;/p&gt;
&lt;h3&gt;仮説の立案と検証の変更&lt;/h3&gt;
&lt;p&gt;観測結果を踏まえ、「子要素の横幅を親要素の内容領域に追従させつつ、画面が広いときだけ上限幅を適用すれば、はみ出しを防げる」という仮説を立てて検証します。&lt;/p&gt;
&lt;p&gt;教材画面のボタン操作により、カード要素のクラス名&lt;code&gt;.fixed&lt;/code&gt;を&lt;code&gt;.responsive&lt;/code&gt;へ切り替えました。このクラスには&lt;code&gt;width: 100%&lt;/code&gt;および&lt;code&gt;max-width: 520px&lt;/code&gt;が指定されています。
変更の結果、カードの横幅は親要素の内容幅332px（360pxから左右の枠線4pxとpadding24pxを除いた領域）に合わせて縮小し、内部の文章も枠線の手前で自然に折り返されました。状態表示欄の文言も「カード：width: 100%; max-width: 520px。親の内容領域に収まります。」へ更新されました。&lt;/p&gt;
&lt;p&gt;同じ文章と親要素の条件を維持して幅指定を変えた結果、掲載した画面のはみ出しは解消しました。別の長さの文章や狭い親要素でも収まることまで、この結果だけで保証することはできません。&lt;/p&gt;
&lt;h3&gt;ソース修正と一時編集の違い&lt;/h3&gt;
&lt;p&gt;画面上でクラスを付け替える操作は、メモリ上に展開されたDOMやスタイルルールに対する一時編集です。ページを再読み込みすれば元の状態に戻ります。&lt;/p&gt;
&lt;p&gt;開発チームが本番環境へ反映すべき作業は、リポジトリ管理下にあるHTMLやCSSファイルを書き換えるソース修正です。調査報告には「どのファイルの何行目をどのように書き換えるべきか」という具体的な差分指示を記載します。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/overrides&quot; rel=&quot;noopener noreferrer&quot;&gt;Chrome DevTools の Local Overrides 機能&lt;/a&gt;を使用すると、ネットワーク経由で取得したファイルの内容をローカルディスク上のファイルで上書きし、再読み込み後も変更を保持したまま検証できます。ただし、手元のブラウザー内で応答を疑似的に置き換える仕組みであり、Webサーバー上の元ファイルを更新するわけではありません。ソースコード本体への反映手順は別途まとめる必要があります。&lt;/p&gt;
&lt;h3&gt;画面前後の記録と機密情報のマスキング&lt;/h3&gt;
&lt;p&gt;修正の効果を伝えるため、変更前と変更後の画面状態を提示します。
報告票に画像を添付する際は、個人情報や環境固有の機密文字列を塗りつぶすか、ダミーデータを用いた再現環境で撮影します。今回の教材画面は実在しない架空の商品名とローカル完結のスタイル構造のみを使用しており、外部サーバーへの通信や個人情報の露出は発生しません。&lt;/p&gt;
&lt;h3&gt;恒久対策の提案コード&lt;/h3&gt;
&lt;p&gt;調査結果から導き出された修正案をコードの形で提示します。次のCSS定義は、固定幅によるはみ出しを解消するために作成した修正案です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* 提案コード（実環境でのビルド確認は未実施） */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.card-container&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  max-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;520&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  box-sizing&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;border-box&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このCSSコードは本番のビルド環境や実運用のスタイルシートへ組み込んでの動作確認は行っておらず、手元での検証結果に基づき提示された未実行の提案コードです。このクラス名は説明用です。実際のHTMLの対象クラスへ結び付けなければ適用されません。対象要素と未実行であることを明記します。&lt;/p&gt;
&lt;h3&gt;開発担当者への引き渡し項目&lt;/h3&gt;
&lt;p&gt;調査報告票には、以下の項目を整理して盛り込みます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;対象画面とURL&lt;/strong&gt;：問題が発生する正確なパスとクエリパラメータ（&lt;code&gt;http://127.0.0.1:9033/demo/index.html?case=responsive&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;発生箇所の要素&lt;/strong&gt;：セレクタとクラス名。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;観測された数値&lt;/strong&gt;：親要素の計算幅と子要素の固定幅の差異。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推奨される修正方針&lt;/strong&gt;：固定値からパーセンテージおよび最大幅指定への移行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;影響調査の依頼&lt;/strong&gt;：同一のクラスやスタイルを共通利用している別画面の有無の確認。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;確認できなかったこと&lt;/h3&gt;
&lt;p&gt;手元の特定環境で再現と解消が確認できたからといって、あらゆる条件下で同じ結果が得られるとは限りません。&lt;/p&gt;
&lt;p&gt;DevToolsのパネル操作とそこでの数値取得は今回実施しておらず、公式資料と教材ソースに基づく追試手順として記述しました。今回の検証環境はmacOS上のGoogle Chromeのみであり、WindowsやLinux環境、SafariやFirefoxなどの他ブラウザーでの表示差異は確認していません。スマートフォンの実機によるタッチ操作や画面回転時の挙動、高DPIディスプレイでの表示確認も未実施です。スクリーンリーダーによる読み上げ、ネットワーク速度制限下での読み込み順序の影響、ページの描画負荷やメモリ消費を測る性能計測も未確認です。&lt;/p&gt;
&lt;p&gt;調査報告票の末尾には、こうした未確認の事項を明記する欄を必ず設けます。&lt;/p&gt;
</content:encoded></item><item><title>JavaScriptとNode.jsのJSON実践ガイド：型の変換から安全なデータの受け渡しまで</title><link>https://blog.yurubo.com/javascript-nodejs-json-practical-guide/</link><guid isPermaLink="true">https://blog.yurubo.com/javascript-nodejs-json-practical-guide/</guid><description>WebアプリケーションやAPI通信、設定ファイルの管理において、JSON（JavaScript Object Notation）はデファクトスタンダードとして日常的に利用されています。JavaScriptのオブジェクトリテラルと見た目が酷似しているため直感的に扱える反面、言語内の型体系とJSON仕様のデータモデルの間に…</description><pubDate>Sat, 19 Sep 2026 05:38:03 GMT</pubDate><content:encoded>&lt;p&gt;WebアプリケーションやAPI通信、設定ファイルの管理において、JSON（JavaScript Object Notation）はデファクトスタンダードとして日常的に利用されています。JavaScriptのオブジェクトリテラルと見た目が酷似しているため直感的に扱える反面、言語内の型体系とJSON仕様のデータモデルの間には明確な差異が存在します。&lt;/p&gt;
&lt;p&gt;この差異を意識せずに扱っていると、意図しないプロパティの欠落、数値の丸めによる精度落ち、予期しない例外の発生といったトラブルに直面します。本稿では、RFC 8259およびECMAScript仕様に基づきながら、JavaScriptおよびNode.js環境におけるJSONのシリアライズ・デシリアライズの仕組み、内部挙動、エッジケース、そしてファイル入出力時の注意点までを検証結果とともに深掘りします。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. JSON仕様とJavaScriptの型体系のギャップ&lt;/h2&gt;
&lt;h3&gt;1.1 RFC 8259の定義と値型&lt;/h3&gt;
&lt;p&gt;IETFによる規格 &lt;a href=&quot;https://www.rfc-editor.org/info/rfc8259/&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 8259&lt;/a&gt; では、JSONを軽量でテキストベース、かつ言語非依存なデータ交換フォーマットと規定しており、ECMAScriptプログラミング言語標準から派生したと説明されています。&lt;/p&gt;
&lt;p&gt;RFC 8259で定義されているJSONの値（Value）型は、以下の6種類に限られます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;object&lt;/strong&gt;（キーと値のペアの順序なしコレクション）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;array&lt;/strong&gt;（値の順序付きリスト）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;number&lt;/strong&gt;（10進表記の数値）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;string&lt;/strong&gt;（Unicode文字列）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;true / false&lt;/strong&gt;（真偽値リテラル）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;null&lt;/strong&gt;（ヌルリテラル）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JavaScriptで扱う値には、&lt;code&gt;undefined&lt;/code&gt;、関数、&lt;code&gt;Symbol&lt;/code&gt;、&lt;code&gt;BigInt&lt;/code&gt; などもあります。関数は呼び出し可能なオブジェクトであり、ここでは値の性質に応じて分けて説明します。JSONにはこれらをそのまま表す専用の型がありません。&lt;a href=&quot;https://tc39.es/ecma262/multipage/structured-data.html&quot; rel=&quot;noopener noreferrer&quot;&gt;ECMAScriptのJSON仕様&lt;/a&gt;と、実際に変換した結果を対応づけて見ていきます。&lt;/p&gt;
&lt;h3&gt;1.2 数値（Number）の扱いの違い&lt;/h3&gt;
&lt;p&gt;RFC 8259の文法定義における数値（&lt;code&gt;number&lt;/code&gt;）には、JavaScriptでおなじみの &lt;code&gt;NaN&lt;/code&gt;（Not a Number）や &lt;code&gt;Infinity&lt;/code&gt;（無限大）は定義されていません。&lt;/p&gt;
&lt;p&gt;後述の検証でも示す通り、&lt;code&gt;JSON.parse(&apos;{&quot;a&quot;: NaN}&apos;)&lt;/code&gt; や &lt;code&gt;JSON.parse(&apos;{&quot;a&quot;: Infinity}&apos;)&lt;/code&gt; を実行すると構文エラー（&lt;code&gt;SyntaxError&lt;/code&gt;）となります。また、&lt;code&gt;JSON.stringify&lt;/code&gt; で &lt;code&gt;NaN&lt;/code&gt; や &lt;code&gt;Infinity&lt;/code&gt; をシリアライズしようとすると、例外をスローするのではなく &lt;code&gt;null&lt;/code&gt; に置き換わって出力されます。&lt;/p&gt;
&lt;h3&gt;1.3 オブジェクトのキーと重複キー&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc8259#section-4&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 8259の第4節&lt;/a&gt;は、名前を文字列とし、同じオブジェクト内では名前を一意にすることを推奨しています。重複した名前を受け取ると、最後の値だけを残す、エラーにする、重複も含めて返すなど、受信側によって結果が変わり得ると説明しています。送信側で名前の重複を避け、特定のパーサーの処理結果に依存しないデータを作る方針が適しています。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 実機検証環境とテスト概要&lt;/h2&gt;
&lt;p&gt;本稿で解説するコード例および挙動の検証は、以下のサンドボックス環境で実施しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js バージョン&lt;/strong&gt;: &lt;code&gt;v22.23.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ネットワークモード&lt;/strong&gt;: none（外部通信なし）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;実行形式&lt;/strong&gt;: ECMAScript Modules (&lt;code&gt;.mjs&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Node runtime: v22.23.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;
&lt;h2&gt;3. JSON.stringifyの変換挙動とエッジケース&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; は、ECMAScriptのStructured Dataセクションに定義されているアルゴリズムに従ってJavaScriptの値をJSONテキストにシリアライズします。値の型やコンテキスト（オブジェクトのプロパティか、配列の要素か、トップレベルか）によって、その挙動は大きく分岐します。&lt;/p&gt;
&lt;h3&gt;3.1 undefined、Function、Symbolの脱落とnull化&lt;/h3&gt;
&lt;p&gt;JavaScript特有の型である &lt;code&gt;undefined&lt;/code&gt;、関数（&lt;code&gt;Function&lt;/code&gt;）、シンボル（&lt;code&gt;Symbol&lt;/code&gt;）は、JSONの6つの値型に対応しないため、以下のような特別な変換規則が適用されます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;オブジェクトのプロパティ値の場合&lt;/strong&gt;: そのプロパティ自体がシリアライズ結果から&lt;strong&gt;省略&lt;/strong&gt;されます。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;トップレベルの値の場合&lt;/strong&gt;: 文字列ではなくJavaScriptの &lt;code&gt;undefined&lt;/code&gt; を返します。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配列の要素の場合&lt;/strong&gt;: 添字の整合性を保つため、&lt;code&gt;null&lt;/code&gt; に変換されます。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以下は実行した検証スクリプトの抜粋です。途中のケースやコメントを省略した箇所があります。assertを使うファイルの先頭では &lt;code&gt;import assert from &quot;node:assert/strict&quot;&lt;/code&gt; を読み込んでいます。抜粋中の変数は同じ節の前のコードから引き継ぐ場合があるため、各断片を単独の完成したプログラムとは扱わないでください。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// A-1: object property with undefined value is omitted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const objUndef = { a: 1, b: undefined };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(objUndef);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;A-1 obj undefined omit:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;{&quot;a&quot;:1}&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// A-4: top-level undefined returns undefined (not string)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r4 = JSON.stringify(undefined);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;A-4 top-level undefined:&apos;, r4);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r4, undefined);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// A-7: array element undefined -&amp;gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const arrUndef = [1, undefined, 3];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r7 = JSON.stringify(arrUndef);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;A-7 array undefined-&amp;gt;null:&apos;, r7);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r7, &apos;[1,null,3]&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果は以下のとおりです。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-1 obj undefined omit: {&quot;a&quot;:1}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-2 obj function omit: {&quot;a&quot;:1}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-3 obj symbol omit: {&quot;a&quot;:1}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-4 top-level undefined: undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-5 top-level function: undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-6 top-level symbol: undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-7 array undefined-&amp;gt;null: [1,null,3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-8 array function-&amp;gt;null: [1,null,3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A-9 array symbol-&amp;gt;null: [1,null,3]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;オブジェクトのプロパティではキーごと削除されるのに対し、配列内では要素数が変わらないように &lt;code&gt;null&lt;/code&gt; で埋められる点が重要なトレードオフです。&lt;/p&gt;
&lt;h3&gt;3.2 NaNとInfinityのシリアライズ&lt;/h3&gt;
&lt;p&gt;前述のとおり、JSONでは &lt;code&gt;NaN&lt;/code&gt; や &lt;code&gt;Infinity&lt;/code&gt; は許可されていません。&lt;code&gt;JSON.stringify&lt;/code&gt; にこれらを渡すと、例外を出さずに &lt;code&gt;null&lt;/code&gt; にシリアライズされます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// B-1: NaN -&amp;gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(NaN);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;B-1 NaN-&amp;gt;null:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;null&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// B-2: Infinity -&amp;gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r2 = JSON.stringify(Infinity);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;B-2 Infinity-&amp;gt;null:&apos;, r2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r2, &apos;null&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// B-4: inside object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r4 = JSON.stringify({ a: NaN, b: Infinity });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;B-4 obj NaN/Inf-&amp;gt;null:&apos;, r4);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r4, &apos;{&quot;a&quot;:null,&quot;b&quot;:null}&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;B-1 NaN-&amp;gt;null: null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;B-2 Infinity-&amp;gt;null: null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;B-3 -Infinity-&amp;gt;null: null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;B-4 obj NaN/Inf-&amp;gt;null: {&quot;a&quot;:null,&quot;b&quot;:null}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;B-5 arr NaN/Inf-&amp;gt;null: [null,null,null]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;数理計算の結果として発生した &lt;code&gt;NaN&lt;/code&gt; や無限大が、シリアライズを経て &lt;code&gt;null&lt;/code&gt; に化けるため、受信側で「値が存在しない」と誤認されるリスクに留意する必要があります。&lt;/p&gt;
&lt;h3&gt;3.3 BigIntによるTypeError&lt;/h3&gt;
&lt;p&gt;標準の &lt;code&gt;JSON.stringify&lt;/code&gt; に変換用の処理を指定せず &lt;code&gt;BigInt&lt;/code&gt; を渡すと、&lt;code&gt;TypeError&lt;/code&gt; が発生します。以下では &lt;code&gt;toJSON&lt;/code&gt; の追加や &lt;code&gt;replacer&lt;/code&gt; による事前変換をしていない場合を確認しています。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// C-1: top-level BigInt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  JSON.stringify(42n);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assert.fail(&apos;should have thrown&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} catch(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  console.log(&apos;C-1 BigInt top-level throws:&apos;, e.constructor.name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assert.ok(e instanceof TypeError, `expected TypeError, got ${e.constructor.name}`);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;C-1 BigInt top-level throws: TypeError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;C-2 BigInt in object throws: TypeError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;C-3 BigInt in array throws: TypeError&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この検証条件では、トップレベル、オブジェクト内部、配列内部のいずれでも &lt;code&gt;TypeError&lt;/code&gt; が発生しました。データベースの64ビット整数IDなどを扱う際は、後述する文字列変換やカスタムの &lt;code&gt;toJSON&lt;/code&gt; メソッドによる対策が必須となります。&lt;/p&gt;
&lt;h3&gt;3.4 循環参照（Circular Reference）の検出&lt;/h3&gt;
&lt;p&gt;オブジェクトが自分自身や祖先ノードを参照している場合、シリアライズ処理が無限ループに陥るのを防ぐため、JavaScriptエンジンは構造を検知して &lt;code&gt;TypeError&lt;/code&gt; をスローします。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// D-1: direct circular&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const obj = { a: 1 };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;obj.self = obj;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  JSON.stringify(obj);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assert.fail(&apos;should have thrown&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} catch(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  console.log(&apos;D-1 circular throws:&apos;, e.constructor.name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assert.ok(e instanceof TypeError);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;D-1 circular throws: TypeError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;D-2 indirect circular throws: TypeError&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;親子関係を持つ木構造のデータやDOM/ORMモデルなどをJSON化する際には、事前に循環参照を解消するか、必要なプロパティのみを抽出する設計が求められます。&lt;/p&gt;
&lt;h3&gt;3.5 疎配列（Sparse Array）の処理&lt;/h3&gt;
&lt;p&gt;要素が飛び飛びになっている疎配列（&lt;code&gt;[1, , 3]&lt;/code&gt; など）をシリアライズした場合、空きスロット（hole）は &lt;code&gt;null&lt;/code&gt; として出力されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// E-1: sparse array with hole&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const arr = [1, , 3];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;arr length:&apos;, arr.length, &apos;arr[1]:&apos;, arr[1]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(arr);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;E-1 sparse array:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;[1,null,3]&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;arr length: 3 arr[1]: undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;E-1 sparse array: [1,null,3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;E-2 new Array holes: [&quot;a&quot;,null,&quot;c&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;
&lt;h2&gt;4. DateオブジェクトとtoJSONメソッドの協調&lt;/h2&gt;
&lt;h3&gt;4.1 Date.prototype.toJSON&lt;/h3&gt;
&lt;p&gt;JavaScript標準の組み込みオブジェクトである &lt;code&gt;Date&lt;/code&gt; には、プロトタイプ上に &lt;code&gt;toJSON&lt;/code&gt; メソッドが定義されています。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const d = new Date(&apos;2024-01-15T12:00:00.000Z&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(typeof d.toJSON, &apos;function&apos;, &apos;Date has toJSON&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;F-1 Date.toJSON exists:&apos;, typeof d.toJSON);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const iso = d.toJSON();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;F-2 toJSON():&apos;, iso);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(iso, &apos;2024-01-15T12:00:00.000Z&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r3 = JSON.stringify(d);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;F-3 stringify Date:&apos;, r3);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r3, &apos;&quot;2024-01-15T12:00:00.000Z&quot;&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;F-1 Date.toJSON exists: function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;F-2 toJSON(): 2024-01-15T12:00:00.000Z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;F-3 stringify Date: &quot;2024-01-15T12:00:00.000Z&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;F-4 Date in obj: {&quot;ts&quot;:&quot;2024-01-15T12:00:00.000Z&quot;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; は、シリアライズ対象のオブジェクトが &lt;code&gt;toJSON&lt;/code&gt; メソッドを持っている場合、まずそのメソッドを呼び出し、その戻り値をシリアライズします。今回のように有効な日時を持つ &lt;code&gt;Date&lt;/code&gt; の場合、&lt;code&gt;toISOString()&lt;/code&gt; と同じ日時文字列が返されるため、結果としてダブルクォートで囲まれた日時文字列が出力されます。&lt;/p&gt;
&lt;h3&gt;4.2 独自オブジェクトでのtoJSONの実装&lt;/h3&gt;
&lt;p&gt;この仕組みは、ユーザー定義のクラスやオブジェクトでも活用できます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// L-1: custom toJSON on plain object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const obj1 = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  internal: &apos;hidden&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  public: &apos;visible&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  toJSON() { return { public: this.public }; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(obj1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;L-1 custom toJSON:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;{&quot;public&quot;:&quot;visible&quot;}&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// L-2: toJSON receives the key it is serialized under&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const obj2 = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  value: 42,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  toJSON(key) { return `key=${key},val=${this.value}`; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r2 = JSON.stringify({ nested: obj2 });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;L-2 toJSON key arg:&apos;, r2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r2, &apos;{&quot;nested&quot;:&quot;key=nested,val=42&quot;}&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;L-1 custom toJSON: {&quot;public&quot;:&quot;visible&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;L-2 toJSON key arg: {&quot;nested&quot;:&quot;key=nested,val=42&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;L-3 replacer after toJSON: {&quot;a&quot;:1,&quot;b&quot;:&quot;added_by_toJSON&quot;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;toJSON&lt;/code&gt; メソッドには、自身がシリアライズされているプロパティのキー名（トップレベルの場合は空文字列 &lt;code&gt;&quot;&quot;&lt;/code&gt;）が引数として渡されます。内部の秘密情報を除外したり、複合データモデルをフラットなオブジェクトに変換して出力したりする際に有用です。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. JSON.stringifyの高度な引数（replacer と space）&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;JSON.stringify&lt;/code&gt; は第2引数に &lt;code&gt;replacer&lt;/code&gt;、第3引数に &lt;code&gt;space&lt;/code&gt; を受け取ることができます。&lt;/p&gt;
&lt;p&gt;引数の指定順は次のとおりです。角括弧は省略可能な引数を表す説明用の記法で、実行するコードではありません。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;JSON.stringify(value[, replacer[, space]])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;5.1 replacer（関数または配列）&lt;/h3&gt;
&lt;h4&gt;関数によるフィルタリング・変換&lt;/h4&gt;
&lt;p&gt;関数を指定した場合、オブジェクトツリーの各キー・値ペアに対してその関数が再帰的に呼び出されます。トップレベルではキーとして空文字列 &lt;code&gt;&quot;&quot;&lt;/code&gt; が渡されます。オブジェクトのプロパティに対して &lt;code&gt;undefined&lt;/code&gt; を返すと、そのプロパティは出力から省略されます。配列要素では &lt;code&gt;null&lt;/code&gt; となり、ルートの値に対して返すと &lt;code&gt;JSON.stringify&lt;/code&gt; 全体の戻り値が &lt;code&gt;undefined&lt;/code&gt; になります。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const obj = { a: 1, b: &apos;hello&apos;, c: true, d: null };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// G-1: replacer function omits key b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(obj, (key, val) =&amp;gt; key === &apos;b&apos; ? undefined : val);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;G-1 replacer omit b:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;{&quot;a&quot;:1,&quot;c&quot;:true,&quot;d&quot;:null}&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;G-1 replacer omit b: {&quot;a&quot;:1,&quot;c&quot;:true,&quot;d&quot;:null}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;配列によるホワイトリスト指定&lt;/h4&gt;
&lt;p&gt;文字列または数値の配列を渡した場合、オブジェクトでは、配列に含まれる名前のプロパティが出力対象になります。この指定は配列の要素番号を選ぶための一覧ではありません。以下の実験は文字列の名前を使ったオブジェクトの例です。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// G-2: replacer array - only include a and c&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r2 = JSON.stringify(obj, [&apos;a&apos;, &apos;c&apos;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;G-2 replacer array [a,c]:&apos;, r2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r2, &apos;{&quot;a&quot;:1,&quot;c&quot;:true}&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;G-2 replacer array [a,c]: {&quot;a&quot;:1,&quot;c&quot;:true}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;5.2 toJSONとreplacerの評価順序&lt;/h3&gt;
&lt;p&gt;オブジェクトが &lt;code&gt;toJSON&lt;/code&gt; メソッドを持ち、かつ &lt;code&gt;JSON.stringify&lt;/code&gt; の第2引数に &lt;code&gt;replacer&lt;/code&gt; が指定されている場合、&lt;strong&gt;まず &lt;code&gt;toJSON&lt;/code&gt; メソッドが実行され、その戻り値に対して &lt;code&gt;replacer&lt;/code&gt; が適用されます&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const calls = [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const obj = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  x: 10,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  toJSON() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    calls.push(&apos;toJSON&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return { x: this.x, added: &apos;fromToJSON&apos; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const replacer = (key, val) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  calls.push(`replacer:${key}`);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return val;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const result = JSON.stringify(obj, replacer);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;N-1 call order:&apos;, calls);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;N-1 result:&apos;, result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(calls[0], &apos;toJSON&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(calls[1], &apos;replacer:&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.ok(calls.some(c =&amp;gt; c === &apos;replacer:added&apos;), &apos;replacer sees added from toJSON&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;N-1 call order: [ &apos;toJSON&apos;, &apos;replacer:&apos;, &apos;replacer:x&apos;, &apos;replacer:added&apos; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;N-1 result: {&quot;x&quot;:10,&quot;added&quot;:&quot;fromToJSON&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;N-2 replacer array after toJSON: {&quot;a&quot;:1,&quot;c&quot;:3}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ログから明らかなように、&lt;code&gt;toJSON&lt;/code&gt; が先行して呼び出され、&lt;code&gt;toJSON&lt;/code&gt; によって追加されたプロパティ &lt;code&gt;added&lt;/code&gt; も後続の &lt;code&gt;replacer&lt;/code&gt; の呼び出し対象に含まれていることが分かります。&lt;/p&gt;
&lt;h3&gt;5.3 spaceによる整形と上限クランプ&lt;/h3&gt;
&lt;p&gt;第3引数の &lt;code&gt;space&lt;/code&gt; に数値を渡すと、出力されるJSONが改行および指定した文字数のインデントで整形されます。この数値の有効範囲は最大10であり、10を超える数値を渡しても自動的に10に切り詰め（クランプ）されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// G-4: space = 10 (max)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r4 = JSON.stringify({ x: 1 }, null, 10);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const lines4 = r4.split(&apos;\n&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;G-4 space=10 line2 leading spaces:&apos;, lines4[1].match(/^ +/)?.[0].length);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(lines4[1].match(/^ +/)?.[0].length, 10);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// G-5: space = 11 should be clamped to 10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r5 = JSON.stringify({ x: 1 }, null, 11);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const lines5 = r5.split(&apos;\n&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;G-5 space=11 clamped to 10, leading spaces:&apos;, lines5[1].match(/^ +/)?.[0].length);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(lines5[1].match(/^ +/)?.[0].length, 10);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;G-4 space=10 line2 leading spaces: 10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;G-5 space=11 clamped to 10, leading spaces: 10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;G-6 space=tab: &quot;{\n\t\&quot;x\&quot;: 1\n}&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;
&lt;h2&gt;6. JSON.parseの構文検査とreviver&lt;/h2&gt;
&lt;h3&gt;6.1 不正なJSONとSyntaxError&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;JSON.parse&lt;/code&gt; は厳密な構文解析を行います。JavaScriptのリテラルとしては許容される表現（単一引用符、末尾のカンマ、プロパティ名のクォート省略、&lt;code&gt;undefined&lt;/code&gt; や &lt;code&gt;NaN&lt;/code&gt; の埋め込みなど）も、JSON仕様に合致しない場合はすべて &lt;code&gt;SyntaxError&lt;/code&gt; になります。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const badInputs = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;{bad}&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;{&quot;a&quot;: undefined}&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;undefined&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;{&apos;a&apos;: 1}&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;{&quot;a&quot;: NaN}&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;{&quot;a&quot;: Infinity}&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &apos;[1, 2,]&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;badInputs.forEach((input, i) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    JSON.parse(input);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    assert.fail(`H-${i+1} should have thrown for input: ${input}`);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } catch(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.log(`H-${i+1} SyntaxError for [${input}]:`, e.constructor.name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    assert.ok(e instanceof SyntaxError, `expected SyntaxError, got ${e.constructor.name}`);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-1 SyntaxError for [{bad}]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-2 SyntaxError for [{&quot;a&quot;: undefined}]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-3 SyntaxError for [undefined]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-4 SyntaxError for [{&apos;a&apos;: 1}]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-5 SyntaxError for [{&quot;a&quot;: NaN}]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-6 SyntaxError for [{&quot;a&quot;: Infinity}]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-7 SyntaxError for []: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-8 SyntaxError for [[1, 2,]]: SyntaxError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;H-9 valid JSON parse: {&quot;a&quot;:1,&quot;b&quot;:&quot;hello&quot;,&quot;c&quot;:null,&quot;d&quot;:true}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;6.2 reviver引数による復元と削除&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;JSON.parse&lt;/code&gt; の第2引数である &lt;code&gt;reviver&lt;/code&gt; 関数を使用すると、構文解析されたオブジェクトツリーを葉（leaf）から根（root）へボトムアップに巡回しながら値を変換できます。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;特定フォーマットの復元&lt;/strong&gt;: ISO 8601文字列を検知して &lt;code&gt;Date&lt;/code&gt; インスタンスへ変換する。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;キーの削除&lt;/strong&gt;: &lt;code&gt;reviver&lt;/code&gt; 関数が &lt;code&gt;undefined&lt;/code&gt; を返すと、そのプロパティは親オブジェクトから削除されます。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// I-1: reviver converts ISO string to Date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const isoReviver = (key, val) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (typeof val === &apos;string&apos; &amp;amp;&amp;amp; /^\d{4}-\d{2}-\d{2}T/.test(val)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return new Date(val);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return val;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.parse(&apos;{&quot;ts&quot;:&quot;2024-01-15T12:00:00.000Z&quot;,&quot;name&quot;:&quot;test&quot;}&apos;, isoReviver);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;I-1 reviver Date:&apos;, r1.ts instanceof Date, r1.ts.toISOString());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.ok(r1.ts instanceof Date);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// I-2: reviver returning undefined deletes the key&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const deleteReviver = (key, val) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (key === &apos;secret&apos;) return undefined;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return val;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r2 = JSON.parse(&apos;{&quot;a&quot;:1,&quot;secret&quot;:&quot;pwd&quot;,&quot;b&quot;:2}&apos;, deleteReviver);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;I-2 reviver delete key:&apos;, JSON.stringify(r2));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(JSON.stringify(r2), &apos;{&quot;a&quot;:1,&quot;b&quot;:2}&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.ok(!(&apos;secret&apos; in r2));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I-1 reviver Date: true 2024-01-15T12:00:00.000Z&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I-2 reviver delete key: {&quot;a&quot;:1,&quot;b&quot;:2}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I-3 reviver order (b before a before &quot;&quot;): [ &apos;b&apos;, &apos;a&apos;, &apos;&apos; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I-4 reviver bigint id: 9007199254740993n bigint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この日時変換の正規表現は、先頭が日時らしい文字列を拾う例であり、厳密な入力検証器ではありません。実際の入力では、変換対象のフィールドを決め、許可する形式や無効な日時の扱いも定義します。ここで使った固定の日時文字列が変換できたことと、任意の入力を安全に受け付けられることは別です。&lt;/p&gt;
&lt;p&gt;巡回順序（I-3）を確認すると、ネストされた内側のプロパティ &lt;code&gt;b&lt;/code&gt;、外側の &lt;code&gt;a&lt;/code&gt;、そしてルートである空文字列 &lt;code&gt;&quot;&quot;&lt;/code&gt; という順で呼び出されていることが分かります。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 数値の精度限界：IEEE 754と巨大な整数&lt;/h2&gt;
&lt;h3&gt;7.1 倍精度浮動小数点数の限界&lt;/h3&gt;
&lt;p&gt;JavaScriptの &lt;code&gt;Number&lt;/code&gt; 型は、IEEE 754倍精度浮動小数点数（64ビット）として実装されています。そのため、正確に表現できる整数の範囲には上限が存在します。&lt;/p&gt;
&lt;p&gt;ECMAScript仕様で定義されている &lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt; は &lt;code&gt;2**53 - 1&lt;/code&gt;（&lt;code&gt;9007199254740991&lt;/code&gt;）です。この範囲を超えると、隣り合う整数をすべて区別できる保証がなくなります。範囲外の整数が必ず丸められるわけではなく、次のログにある &lt;code&gt;9007199254740992&lt;/code&gt;（2の53乗）は正確に表現できます。一方、その次の &lt;code&gt;9007199254740993&lt;/code&gt; は同じ値へ丸められます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// J-1: Number.MAX_SAFE_INTEGER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;J-1 MAX_SAFE_INTEGER:&apos;, Number.MAX_SAFE_INTEGER);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(Number.MAX_SAFE_INTEGER, 9007199254740991);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// J-4: large integer in JSON string loses precision on parse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const largeIdStr = &apos;9007199254740993&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const parsedLarge = JSON.parse(largeIdStr);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;J-4 parse large int string:&apos;, parsedLarge, &apos;type:&apos;, typeof parsedLarge);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;J-4 parsedLarge === 9007199254740992:&apos;, parsedLarge === 9007199254740992);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(typeof parsedLarge, &apos;number&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(parsedLarge, 9007199254740992);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.notEqual(BigInt(parsedLarge), 9007199254740993n);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-1 MAX_SAFE_INTEGER: 9007199254740991&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-2 2^53 parse: 9007199254740992&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-3 bigId stringify: 9007199254740992&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-3 literal already rounded: 9007199254740992 9007199254740992 true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-4 parse large int string: 9007199254740992 type: number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-4 parsedLarge === 9007199254740992: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;J-5 string-encoded large ID: 9007199254740993 string&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;7.2 実装間の相互運用性とベストプラクティス&lt;/h3&gt;
&lt;p&gt;上記の結果が示す通り、文字列 &lt;code&gt;&apos;9007199254740993&apos;&lt;/code&gt; を &lt;code&gt;JSON.parse&lt;/code&gt; すると、丸めが発生して &lt;code&gt;9007199254740992&lt;/code&gt; に化けてしまいます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc8259#section-6&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 8259の第6節&lt;/a&gt;は、広く使われるIEEE 754 binary64の範囲と精度を超える値で、相互運用上の問題が起こり得ると説明しています。また、整数については &lt;code&gt;-(2**53)+1&lt;/code&gt; から &lt;code&gt;(2**53)-1&lt;/code&gt; の範囲で数値が正確に一致するとしています。「15桁を超えたら一律に使えない」という制限ではありません。&lt;/p&gt;
&lt;p&gt;安全な整数範囲を超える可能性のある64ビット整数IDを扱う場合は、JSON内では数値リテラルではなく&lt;strong&gt;文字列&lt;/strong&gt;として伝送し、JavaScript側で &lt;code&gt;BigInt(val)&lt;/code&gt; として復元するのが安全な設計パターンです。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// J-5: string-encoded ID avoids precision loss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const safeStr = &apos;&quot;9007199254740993&quot;&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const parsedSafe = JSON.parse(safeStr);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;J-5 string-encoded large ID:&apos;, parsedSafe, typeof parsedSafe);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(parsedSafe, &apos;9007199254740993&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(typeof parsedSafe, &apos;string&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;
&lt;h2&gt;8. MapとSetのシリアライズにおける落とし穴&lt;/h2&gt;
&lt;p&gt;コレクションを扱う &lt;code&gt;Map&lt;/code&gt; と &lt;code&gt;Set&lt;/code&gt; は、&lt;code&gt;JSON.stringify&lt;/code&gt; にそのまま渡しても意図したデータが出力されません。&lt;/p&gt;
&lt;p&gt;変換用のフックや &lt;code&gt;replacer&lt;/code&gt; 配列を指定しない標準的なオブジェクト変換では、&lt;code&gt;JSON.stringify&lt;/code&gt; は自身が持つ列挙可能な文字列キーのプロパティを対象にします。&lt;code&gt;Map&lt;/code&gt; や &lt;code&gt;Set&lt;/code&gt; の内部要素はプロパティではなく内部スロットに保持されているため、シリアライズすると空のオブジェクト &lt;code&gt;{}&lt;/code&gt; として出力されてしまいます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// K-1: Map stringifies as {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const map = new Map([[&apos;a&apos;, 1], [&apos;b&apos;, 2]]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r1 = JSON.stringify(map);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;K-1 Map stringify:&apos;, r1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r1, &apos;{}&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// K-2: Set stringifies as {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const set = new Set([1, 2, 3]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const r2 = JSON.stringify(set);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;K-2 Set stringify:&apos;, r2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.equal(r2, &apos;{}&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-1 Map stringify: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-2 Set stringify: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-3 Map-&amp;gt;entries: [[&quot;a&quot;,1],[&quot;b&quot;,2]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-4 Map-&amp;gt;Object: {&quot;a&quot;:1,&quot;b&quot;:2}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-5 Set-&amp;gt;Array: [1,2,3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;K-6 Map roundtrip a: 1 b: 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;8.1 明示的な変換パターン&lt;/h3&gt;
&lt;p&gt;今回のような &lt;code&gt;Map&lt;/code&gt; や &lt;code&gt;Set&lt;/code&gt; の要素をJSONに残すには、配列やプレーンなオブジェクトへ変換する方法があります。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// K-3: explicit Map -&amp;gt; array of entries conversion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const mapConverted = JSON.stringify([...map.entries()]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// K-4: explicit Map -&amp;gt; Object.fromEntries&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const mapObj = JSON.stringify(Object.fromEntries(map));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// K-5: explicit Set -&amp;gt; Array.from conversion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const setConverted = JSON.stringify(Array.from(set));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;復元時も同様に、&lt;code&gt;new Map(JSON.parse(mapJson))&lt;/code&gt; や &lt;code&gt;new Set(JSON.parse(setJson))&lt;/code&gt; を明示的に行います。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Node.jsにおけるJSONファイルの入出力（fsモジュール）&lt;/h2&gt;
&lt;p&gt;Node.jsの公式ドキュメント（&lt;a href=&quot;https://nodejs.org/docs/latest-v22.x/api/fs.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Node.js File system API&lt;/a&gt;）に記載されている通り、&lt;code&gt;fs&lt;/code&gt; モジュールは主に3種類のスタイルを提供しています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;同期API&lt;/strong&gt;（&lt;code&gt;readFileSync&lt;/code&gt;, &lt;code&gt;writeFileSync&lt;/code&gt; など）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;コールバックAPI&lt;/strong&gt;（&lt;code&gt;fs.readFile&lt;/code&gt;, &lt;code&gt;fs.writeFile&lt;/code&gt; など）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Promise API&lt;/strong&gt;（&lt;code&gt;fs/promises&lt;/code&gt; の &lt;code&gt;readFile&lt;/code&gt;, &lt;code&gt;writeFile&lt;/code&gt; など）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;9.1 同期API（readFileSync / writeFileSync）での読み書き&lt;/h3&gt;
&lt;p&gt;CLIツールやサーバー起動時の初期化処理など、イベントループのブロックが問題にならない文脈では同期APIが手軽です。ファイル読み書き時にはオプション &lt;code&gt;{ encoding: &apos;utf8&apos; }&lt;/code&gt; を指定することで、バッファ（&lt;code&gt;Buffer&lt;/code&gt;）ではなく文字列として直接データを取得・出力できます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const tmpPath = join(tmpdir(), &apos;test_json_exp.json&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const data = { name: &apos;test&apos;, count: 42, active: true, tags: [&apos;a&apos;, &apos;b&apos;] };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// M-1: sync write and read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fs.writeFileSync(tmpPath, JSON.stringify(data), { encoding: &apos;utf8&apos; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const rawSync = fs.readFileSync(tmpPath, { encoding: &apos;utf8&apos; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const parsedSync = JSON.parse(rawSync);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;M-1 sync roundtrip:&apos;, JSON.stringify(parsedSync));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.deepEqual(parsedSync, data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// M-2: sync write with pretty print&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const prettyPath = join(tmpdir(), &apos;test_json_pretty.json&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fs.writeFileSync(prettyPath, JSON.stringify(data, null, 2), { encoding: &apos;utf8&apos; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const rawPretty = fs.readFileSync(prettyPath, { encoding: &apos;utf8&apos; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;M-2 pretty written (first 20 chars):&apos;, rawPretty.substring(0, 20));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;9.2 Promise API（fs/promises）での非同期読み書き&lt;/h3&gt;
&lt;p&gt;Webサーバーのリクエスト処理中など、同時実行性が重視される環境では &lt;code&gt;fs/promises&lt;/code&gt; を利用します。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// M-4: promise-based read/write&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;await writeFile(tmpPath, JSON.stringify({ async: true, val: 99 }), &apos;utf8&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const rawAsync = await readFile(tmpPath, &apos;utf8&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const parsedAsync = JSON.parse(rawAsync);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;M-4 async roundtrip:&apos;, JSON.stringify(parsedAsync));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;assert.deepEqual(parsedAsync, { async: true, val: 99 });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;実行結果:&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-1 sync roundtrip: {&quot;name&quot;:&quot;test&quot;,&quot;count&quot;:42,&quot;active&quot;:true,&quot;tags&quot;:[&quot;a&quot;,&quot;b&quot;]}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-2 pretty written (first 20 chars): {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;name&quot;: &quot;test&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-3 fs.readFileSync type: function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-3 fs.writeFileSync type: function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-3 readFile (promise) type: function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-3 writeFile (promise) type: function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-4 async roundtrip: {&quot;async&quot;:true,&quot;val&quot;:99}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M-5 fs.readFile (callback) type: function&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;
&lt;h2&gt;10. 安全なデータ受け渡しと運用上のトレードオフ&lt;/h2&gt;
&lt;p&gt;ここまでの挙動を踏まえ、実務で安全にJSONをやり取りするための設計指針を整理します。&lt;/p&gt;
&lt;h3&gt;10.1 シリアライズ時・デシリアライズ時の落とし穴まとめ&lt;/h3&gt;
&lt;p&gt;次の表は独自の変換フックを追加しない場合の整理です。Dateは今回検証した有効な日時、MapとSetは追加の独自プロパティを持たない例を対象にしています。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;JavaScriptの型 / 状態&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;オブジェクトプロパティ時&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;配列要素時&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;トップレベル時&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;復元（parse）時の挙動&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;プロパティごと省略&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;省略された値は復元不可。配列内ではnullが残る&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Function&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;プロパティごと省略&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;省略された値は復元不可。配列内ではnullが残る&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Symbol&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;プロパティごと省略&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;省略された値は復元不可。配列内ではnullが残る&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;NaN&lt;/code&gt; / &lt;code&gt;Infinity&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;数値ではなく &lt;code&gt;null&lt;/code&gt; として復元される&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;BigInt&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;数値の場合は丸めリスク、文字列なら安全&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;循環参照&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TypeError スロー&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;疎配列の空き&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;-&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;-&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;null&lt;/code&gt; の要素として復元される&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Date&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ISO 8601文字列&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ISO 8601文字列&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ISO 8601文字列&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;文字列として復元（Dateには戻らない）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;Map&lt;/code&gt; / &lt;code&gt;Set&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;プレーンな空オブジェクトとして復元される&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;10.2 プロダクションでの実装パターン&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;スキーマバリデーションの導入&lt;/strong&gt;:
&lt;code&gt;JSON.parse&lt;/code&gt; が成功しても、必要な項目や値の範囲がアプリケーションの期待に合うとは限りません。構文エラーは &lt;code&gt;SyntaxError&lt;/code&gt; として処理し、その後で項目の有無、型、範囲を別に検査します。スキーマ検証ライブラリを使う場合も、業務上の制約を定義する必要があります。本稿では特定のライブラリを比較検証していません。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;型安全な日付の復元&lt;/strong&gt;:
&lt;code&gt;reviver&lt;/code&gt; 引数を利用するか、スキーマバリデーションライブラリの変換（Coercion / Transform）パイプラインを利用して、ISO 8601文字列から明示的に &lt;code&gt;Date&lt;/code&gt; オブジェクトへ復元します。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;64ビットIDの文字列化&lt;/strong&gt;:
安全な整数範囲を超える可能性のあるIDは、JSON上では文字列にする契約を送受信側で共有します。計算が不要なら、受信後も文字列のまま扱えます。&lt;code&gt;BigInt&lt;/code&gt; に戻すのは整数として処理する必要がある場合です。UUIDやULIDは64ビット整数と同じ分類ではなく、それぞれの文字列表現を保って扱います。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;確認できなかったこと&lt;/h2&gt;
&lt;p&gt;検証はNode.js v22.23.1と、本文に示した小さな固定入力を対象にしています。
コードとログは掲載箇所を照合しましたが、あらゆる入力や実行環境で同じ結果になることを網羅的に証明したものではありません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仕様と実装の対応範囲&lt;/strong&gt;：RFC 8259の値、オブジェクトの名前、数値の説明は仕様書を参照しました。ECMAScriptの内部アルゴリズムの全ステップとエンジン内部の実装を照合したわけではありません。実測した結果だけから、規格の歴史的な設計理由や内部処理の目的までは断定しません。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;変換フックの組合せ&lt;/strong&gt;：本文では固定のreplacer、reviver、toJSONを使っています。getterやProxy、独自プロトタイプを含むオブジェクト、変換中に構造が変化する場合、フック自身が例外を投げる場合までは試していません。標準状態でのBigIntの例外やMapとSetの出力を、あらゆるカスタマイズ後の挙動に一般化しないでください。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数値と日時の入力範囲&lt;/strong&gt;：大整数の例は整数IDの丸めを示すものです。小数の丸め方式や他言語との相互運用全般は検証していません。日時も固定の有効な値で確認したため、無効な日付、タイムゾーンの境界、任意の形式の文字列の受理可否は別途検査が必要です。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;構文とデータの意味&lt;/strong&gt;：不正なJSONの例を確認していますが、重複した名前を持つJSONの処理を複数のパーサーで比較してはいません。JSON.parseが成功した後の認可、業務ルール、入力サイズ制限を含むセキュリティ対策の網羅検証も対象外です。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ファイル操作と運用条件&lt;/strong&gt;：同期APIとPromise APIの読み書き、およびコールバックAPIの存在を確認しました。コールバックAPIを含む全関数の動作、巨大ファイルでの速度やメモリ消費、同時書込み、書込み途中の障害、データの永続性は検証していません。非同期APIを選ぶだけで競合やデータ損失を防げるとは限りません。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文の例をアプリケーションへ取り込む際は、入力データの契約と、処理に失敗した場合の扱いを先に決めてください。
ここで確認した変換結果を基準に、実際に受け付ける値と拒否する値をテストへ追加すると、シリアライズの前後で失いたくない情報を明示できます。&lt;/p&gt;
</content:encoded></item><item><title>Node.jsのJSON.stringifyはundefinedをどう扱うか：オブジェクトと配列の違い</title><link>https://blog.yurubo.com/node-js-json-stringify-undefined/</link><guid isPermaLink="true">https://blog.yurubo.com/node-js-json-stringify-undefined/</guid><description>Node.jsのJSON.stringifyにおけるundefinedの扱い：オブジェクトと配列の挙動差 JavaScriptの標準メソッドである`JSON.stringify()`を使ってデータをJSON形式の文字列に変換するとき、値として`undefined`が含まれていると、オブジェクト内と配列内で変換結果が大き…</description><pubDate>Sat, 19 Sep 2026 04:48:21 GMT</pubDate><content:encoded>&lt;h1&gt;Node.jsのJSON.stringifyにおけるundefinedの扱い：オブジェクトと配列の挙動差&lt;/h1&gt;
&lt;p&gt;JavaScriptの標準メソッドである&lt;code&gt;JSON.stringify()&lt;/code&gt;を使ってデータをJSON形式の文字列に変換するとき、値として&lt;code&gt;undefined&lt;/code&gt;が含まれていると、オブジェクト内と配列内で変換結果が大きく異なります。&lt;/p&gt;
&lt;p&gt;JSONには&lt;code&gt;undefined&lt;/code&gt;に対応する型が存在しないため、データ構造に応じた調整が行われます。本記事ではNode.js環境での実測をもとに、その挙動の違いを整理します。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;オブジェクトと配列における挙動の違い&lt;/h2&gt;
&lt;h3&gt;1. オブジェクト内のプロパティの場合&lt;/h3&gt;
&lt;p&gt;オブジェクトのプロパティ値に&lt;code&gt;undefined&lt;/code&gt;が指定されている場合、そのプロパティはキーごと出力から省略されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Case 1: Object with undefined property&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const objResult = JSON.stringify({a: undefined, b: null});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;Object result:&apos;, objResult);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;出力の抜粋:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Object result: {&quot;b&quot;:null}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;キー&lt;code&gt;a&lt;/code&gt;は省略され、&lt;code&gt;null&lt;/code&gt;を持つキー&lt;code&gt;b&lt;/code&gt;のみが出力されます。&lt;/p&gt;
&lt;h3&gt;2. 配列の要素の場合&lt;/h3&gt;
&lt;p&gt;配列の要素として&lt;code&gt;undefined&lt;/code&gt;が含まれる場合、要素は削除されず&lt;code&gt;null&lt;/code&gt;に置き換えられます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Case 2: Array with undefined element&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const arrResult = JSON.stringify([undefined, null]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;Array result:&apos;, arrResult);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;出力の抜粋:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Array result: [null,null]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;この結果では要素数は2のままで、undefinedの位置がnullになります。&lt;/p&gt;
&lt;h3&gt;3. トップレベルおよび関数の挙動&lt;/h3&gt;
&lt;p&gt;単体の&lt;code&gt;undefined&lt;/code&gt;を直接渡すと、文字列ではなくJavaScriptの&lt;code&gt;undefined&lt;/code&gt;がそのまま返されます。&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Case 3: Top-level undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const topResult = JSON.stringify(undefined);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.log(&apos;Top-level undefined result:&apos;, topResult, &apos;(type:&apos;, typeof topResult, &apos;)&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;出力の抜粋:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Top-level undefined result: undefined (type: undefined )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;また、関数（Function型）を渡した場合も同様に、オブジェクト内ではプロパティが省略され、配列内では&lt;code&gt;null&lt;/code&gt;に変換されます。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;検証環境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;実行ランタイム&lt;/strong&gt;: Node.js（コンテナイメージ: &lt;code&gt;node-22&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ネットワーク&lt;/strong&gt;: なし（オフライン環境）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;確認できなかったこと&lt;/h2&gt;
&lt;p&gt;今回の検証および参照ドキュメントの範囲内において、以下の事項は実証・確認が完了していません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ECMAScript仕様の内部アルゴリズム&lt;/strong&gt;: 仕様書内の&lt;code&gt;SerializeJSONObject&lt;/code&gt;、&lt;code&gt;SerializeJSONArray&lt;/code&gt;、&lt;code&gt;SerializeJSONProperty&lt;/code&gt;などの内部抽象操作や判定処理の本文（提供情報が目次のみのため未確認）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RFC 8259仕様との直接の関連&lt;/strong&gt;: 配列の整合性維持と&lt;code&gt;null&lt;/code&gt;置換の由来に関するRFC 8259本文との照合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symbol型の網羅的動作&lt;/strong&gt;: Symbol型プロパティや要素に対する挙動の実機検証。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;追加引数の動作&lt;/strong&gt;: 第2引数（replacer）および第3引数（space）を指定した際の挙動。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;参考情報&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN: JSON.stringify()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tc39.es/ecma262/multipage/structured-data.html&quot; rel=&quot;noopener noreferrer&quot;&gt;ECMAScript Language Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>