測れるもので代用すると、測りたいものから離れる
「記事を最後まで読んだ人が何人いるか」を知りたい、というだけの話でした。単純に見えたので、すぐ作れると思っていました。
実際には4回作り直しています。そのたびに別の壊れ方をしました。
読了の判定を作り直した4回ぶんの経緯と、そのたびに何を見落としていたかです。最後に、そもそも何を測ろうとしていたのかを整理します。
1回目:下までスクロールしたかで測った
最初は素直に、ページの何%までスクロールしたかで判定しました。95%まで来たら読了、という形です。
これがうまくいきませんでした。分母にしていたページ全体の高さには、本文の下にある前後記事のリンクや一覧へ戻るリンク、フッターまで含まれています。実測すると、その下側だけで約680pxありました。
本文を最後まで読み終えても、進捗は90%前後にしかなりません。100%にするには、読むもののないフッターまでスクロールする必要がありました。
勢いよくスクロールする人は慣性で最下端まで行くので満たせます。一方、文章を追って読む人は本文の終わりで手を止めます。ゆっくり読む人ほど読了にならないという、目的と逆の結果になっていました。
2回目:本文の高さを分母にした
分母を本文だけに変えました。本文の末尾が画面の下端に来た時点で100%になります。読み終えれば100%、という当たり前の対応になりました。
ただ、これで別の穴が開きます。目次から末尾へ飛んだ場合も100%になります。位置だけを見ている限り、そこに至った経緯は分かりません。
3回目:滞在時間の条件を足した
そこで「そのページを実際に見ていた時間が一定以上ある」を条件に加えました。一瞬で駆け抜けた操作を弾くためです。
条件が2つになった瞬間、画面の表示と記録がずれ始めました。
| 回 | 何で判定したか | そこで抜けたもの |
|---|---|---|
| 1 | ページ全体のスクロール率 | 読む人ほど到達しない |
| 2 | 本文のスクロール率 | 飛んだ人も通る |
| 3 | 本文の到達+滞在時間 | 表示と記録がずれる |
| 4 | 同上(時間を緩和) | 読まずに時間だけで通る |
進捗のバーは位置だけを表しているので、末尾に着いた瞬間に満杯になります。しかし記録は時間の条件を満たすまで行われません。バーは終わっているのに、読了済みにならないという食い違いが画面に残りました。
判定に条件を足すのは簡単ですが、画面に出している表示のほうは1つのままでした。
1つの表示で2つの条件を表そうとすると、必ずどちらかの状態を隠すことになります。隠された側が読み手には「動かない」ように見えます。
間に挟まった、数字の問題
ずれを隠そうとして、記録される前は「あと1%」で止める、という処理を入れました。0%と出しているのに読了済みに変わらない、という矛盾を避けるためです。
これは失敗でした。今度は「1%のまま動かない」という別の分かりにくさになりました。残っていないものを数字で表そうとしたのが、そもそもの間違いでした。
「残り」は数字で表せますが、「末尾には着いたが、まだ記録されていない」は数字ではありません。
ここは数字をやめて、状態をそのまま言葉にするべき場所でした。無理に数値化すると、動かない数字という嘘が残ります。
4回目:今度は、読まずに完了するようになった
時間の条件を緩めたところ、逆の症状が出ました。読み終えていないのに、時間の経過だけで完了になります。
設計上、位置と時間の両方を満たさないと読了にはならないはずです。それでも時間だけで通るということは、位置の条件が常に成立しているとしか考えられません。
2つの条件を「かつ」で結ぶと、片方が常に真になっても、全体としては動き続けます。残った条件が単独の判定として働くだけで、エラーにはなりません。
気づけたのは、症状が「時間だけで通る」という形で表に出たからです。出方が違えば、そのまま気づかずにいた可能性があります。
そもそも、測れるもので代用していた
ここまで来て、やっと本題に気づきました。
知りたいのは「読んだか」です。しかし測れるのは「画面がそこを通ったか」と「そのページが開かれていた時間」だけです。読んだかどうかは、ブラウザからは直接見えません。
つまり最初から、測りたいものを測れるもので代用していました。代用そのものは避けられません。問題は、代用していることを忘れて、条件を足したり引いたりしていたことです。
条件を足すたびに「これは読んだことの何を近似しているのか」を書いていれば、位置と時間が別々の近似だと分かったはずです。
別々の近似を「かつ」で結べば、片方が外れたときに残りが単独で判定を下します。足したのは精度ではなく、壊れ方の種類でした。
いま考えている形
次は、時間をやめて経路を見ようと思っています。
記事をいくつかの区画に区切り、そのすべてが画面を通ったかどうかを判定します。目次から末尾へ飛べば途中の区画を通らないので読了になりません。ゆっくり読む人は必ず全区画を通るので、確実に読了になります。
時間に頼らずに「飛ばした人を通さない」と「速く読む人を弾かない」の両方が満たせます。何より、近似しているものが1種類になるので、片方が壊れて気づかない、という形にはなりません。
今回の学び
まず、測りたいものと測れるものは違うこと。読んだかどうかは見えないので、必ず何かで代用します。代用だと自覚していないと、精度を上げているつもりで壊れ方を増やすことになります。
次に、条件を「かつ」で結ぶと、壊れても動き続けること。片方が常に真になっても例外は出ません。残った条件がそのまま判定として働くので、症状が出るまで気づけません。条件を足すときは、それぞれが単独になったときに何が起きるかを見ておく必要がありました。
そして、判定に条件を足したら、表示も足すか、表示のほうを言い換えること。1つの表示で2つの状態を表そうとすると、必ずどちらかが隠れます。隠れた側は、読み手には「壊れている」ように見えます。
関連記事

長さが合っているのに、中身が壊れていた
画像を分割して送ったら、長さは1文字も違わないのに真っ黒な画像が上がりました。どこで壊れたかを見つけるまでの遠回りと、検証をどこに置くべきかの話です。

奥行きは、影だけの話ではない
ダークモードで影が消えて、奥行きを影だけで作っていたことに気づきました。面を分ける手段を並べて、どれが配色に依存するのか、どう選べばいいのかを整理します。

読む邪魔をしない
貼り付いたヘッダーと目次が、読んでいるあいだずっと画面を占めていた。下へ読むときは引っ込むヘッダー、スクロール中だけ薄くなる目次、先頭へ戻るボタン。3つの判断の記録。