失敗を画面に出す

このレッスンで分かること

  • console.log に出した失敗は、利用者から見れば起きていないのと同じだということ
  • エラーの表示は class の付け外しで切り替え、CSS 側に見た目を任せられること
  • 次の読み込みを始める前に、前の失敗の跡を消す必要があること

失敗を画面に出す とは

catch で受け取った失敗を、コンソールではなく画面の決まった場所に出し、次の操作で消えるところまで作ることです。

ここまでで足りていないこと

前の 2 回で、失敗を受け止める書き方と、失敗の種類を自分で作る書き方をやった。どちらも catch までは書けている。足りていないのは、その先である。

try { render(works); } catch (error) { console.log(error.message); }

これを書いた人は「エラー処理を書いた」と思っている。だが利用者は開発者ツールを開かない。画面には何も出ず、押したボタンは反応せず、作品一覧は空のままである。利用者から見れば、失敗したのではなく壊れている。

失敗の表示は 3 つの問いに答えられていればよい。何が起きたのか、自分のせいなのか、次に何をすればよいのか。この 3 つが 1 文で伝わるなら、それ以上の情報はコンソールに置いてよい。

この回では、作品データの読み込みが失敗したときに、その理由を画面へ出す。次の章から扱う fetch の失敗も、出す場所はここと同じである。

表示の切り替えは class で行う

エラーの表示を出したり消したりするとき、JavaScript から style.display を直接書き換えたくなる。やめたほうがよい。見た目の指定が CSS と JavaScript に分かれて、あとから配色を変える人がどちらを直せばよいか分からなくなる。

CSS 側に状態ごとの見た目を書いておき、JavaScript は class を付け外しするだけにする。入門の第8章で覚えた classList がそのまま使える。

.error-panel { display: none; } .error-panel.is-error { display: block; }
errorPanel.textContent = "作品を読み込めませんでした"; errorPanel.classList.add("is-error");

この形なら、あとで赤い枠を付けたくなっても、揺らす動きを足したくなっても、直すのは CSS だけで済む。

用意してある HTML はこうなっている。role="alert" を付けてあるのは、画面読み上げに使う道具にも「今この文が出た」と伝わるようにするためである。

<p class="error-panel" id="error-panel" role="alert"></p> <div class="work-list" id="work-list"></div>

出すだけでなく、消す

作りはじめでいちばん抜けやすいのが、消すほうである。壊れたデータを読んで赤い枠が出たあと、正しいデータを読み直すと作品は並ぶ。しかし赤い枠を消し忘れると、成功しているのに失敗の文言が残ったままになる。

だから読み込みを始めるところで、まず前回の跡を消す。

する場所すること
読み込みの開始時前の失敗の表示を消す
成功したとき一覧を描き直す
失敗したとき理由を出し、一覧を空にする

「開始時に消す」を 1 か所にまとめておくと、これから読み込みの入口が増えても書き足す必要がない。第9章で読み込み中の表示を足すときも、同じ場所に並べる。

出す文言を決める

error.message をそのまま画面に出すと、利用者に「Unexpected end of JSON input」と見せることになる。伝わらないうえ、環境によって文面が変わる。

出すのは自分で決めた 1 文にして、原因の手がかりだけを添える。今回は error.name を括弧に入れて添える形にする。問い合わせを受けたときに、どの種類の失敗だったかが分かる。

showError("作品を読み込めませんでした (" + error.name + ")");

失敗の種類ごとに文言を変えたいときは、前のレッスンで作った自前のエラークラスが効いてくる。error instanceof ValidationError なら入力のやり直しを促し、そうでなければ時間をおいて試すよう伝える、という分け方ができる。

失敗の種類画面に出す内容
入力の不備どの項目をどう直せばよいか
データの解析に失敗読み込めなかったことと、もう一度試せること
想定外想定外である旨。細かい内容はコンソールへ

一部が壊れても、全部を止めない

読み込み全体を 1 つの try で囲むと、5 件のうち 1 件だけ形がおかしいときにも一覧がまるごと消える。1 件ずつ描く場所を try で囲めば、壊れた 1 件だけを飛ばして残りを出せる。

parsed.forEach(function (work) { try { workList.append(createWorkCard(work)); } catch (error) { skipped = skipped + 1; } });

どちらがよいかは、出せない部分がどれくらい大事かで決まる。作品一覧のように「1 件欠けても意味が通じる」ものは 1 件ずつ囲んでよい。合計金額のように「1 件欠けたら数字が嘘になる」ものは、全体で止めて失敗と伝えるほうが正しい。

部分的に飛ばしたときは、飛ばしたことを黙っていない。何件を出せなかったのか、小さくてよいので画面に残す。

今回の課題は前者ではなく、読み込みそのものが失敗する形で作る。分かれ道があることだけ覚えておいてほしい。

課題ですること

loadWorksText は文字列を返すだけの関数で、"broken" を渡すと途中で切れた JSON を返す。書き換えないこと。書くのは次の 3 本である。

  1. showError(message) で、文を入れて is-error を付け、一覧を空にして is-empty を付ける
  2. clearError() で、文を消して is-erroris-empty を外す
  3. showWorks(kind) で、開始時に clearError を呼び、try の中で解析して並べ、catch で showError を呼ぶ

配列でない中身が返ってきたときも失敗として扱う。Array.isArray で調べて、違えば自分で throw する。JSON としては読めてしまうので、JSON.parse は例外を投げてくれない。ここは自分で見つけて投げる場所である。

判定は 5 つある。押して並ぶこと、壊れたデータで文言が出ること、そのとき一覧が空になること、読み直すと表示が消えること、そして最初は何も出ていないことである。1 つずつ通していけばよい。

なお、このプレビューでは localStorage が使えない。失敗した回数を覚えておきたくなっても、保存の口は呼ばずに変数で持つこと。

よくある間違い

成功したときに前の表示を消していない

function showWorks(kind) { try { const parsed = JSON.parse(loadWorksText(kind)); workList.textContent = ""; parsed.forEach(function (work) { workList.append(createWorkCard(work)); }); } catch (error) { showError("作品を読み込めませんでした"); } }

これは成功したときに is-error を外していない。一度失敗すると、そのあと何回成功しても赤い枠が残る。try に入る前に clearError() を呼ぶ。

エラーの文を alert で出す

catch (error) { alert("読み込みに失敗しました"); }

alert は押すまで消えず、押したら跡が残らない。利用者は文言を読み返せず、画面のどこが失敗したのかも分からない。失敗した場所の近くに、消えない形で出す。

表示を隠すのに要素を消してしまう

errorPanel.remove();

これで見えなくはなるが、次に失敗したときに出す先が無くなる。隠すのと消すのは違う。付け外しするのは class だけにする。

課題

  1. showError(message) で #error-panel に文を入れ、is-error を付ける。一覧は空にして #work-list に is-empty を付ける
  2. clearError() で #error-panel の文を消し、is-error と is-empty を外す
  3. showWorks(kind) は最初に clearError() を呼び、try の中で loadWorksText(kind) の文字列を JSON.parse する
  4. 解析した結果が配列でなければ自分で throw し、catch で showError を呼ぶ
  5. 画面に出す文言は「作品を読み込めませんでした」で始め、括弧の中に error.name を添える

ヒント

script.js
プレビュー

できているか

  • 最初はエラー表示が出ていない
  • 「正しいデータを読む」を押すと作品が並ぶ
  • 「壊れたデータを読む」を押すと理由が画面に出る
  • 成功のあとに失敗すると一覧が空になる
  • 読み直すとエラー表示が消える