ゆっくり読む人ほど、読了にならなかった
「ゆっくり読むと、読了にならない気がする」という指摘をもらいました。
調べたところ、そのとおりでした。丁寧に読む人ほど読了にならず、勢いよくスクロールした人ほど読了になるという、逆の作りになっていました。
割合で測るときに何を分母にするかで意味が変わるという話と、「最後まで到達した」と「読み終えた」を分けて考えた結果です。
元の判定
読了は、こう判定していました。
進捗 = スクロール位置 ÷ (ページ全体の高さ − 画面の高さ)
進捗が 95% を超えたら読了
一見、妥当に見えます。95%にしているのも、末尾ぴったりまで行かなくてよいようにという配慮でした。
実際に測ってみた
推測で直すと、原因が別にあったときに気づけません。実際の記事のページで数字を出しました。
| 測ったもの | 値 |
|---|---|
| ページ全体の高さ | 7,609px |
| 本文の高さ | 6,153px |
| 本文より下に残っている高さ | 684px |
| 本文を読み終えた時点の進捗 | 89.9% |
本文を最後の1行まで読んでも、89.9%にしかなりません。95%に届くには、そこからさらに344px下——前後の記事へのリンクと、フッターの中まで進む必要がありました。
なぜ「ゆっくり」だと届かないのか
ここが今回の肝でした。読む速さと、スクロールの止まる位置には関係があります。
勢いよくスクロールした人
→ 慣性でページの最下端まで滑る → 100% → 読了
文章を追って読んだ人
→ 本文の最後の行で手を止める → 89.9% → 読了にならない
速く動かした指は、そのまま惰性でフッターまで運ばれます。一方、読んでいる人は読むものが無くなった時点で止まります。当然の行動です。
ページ全体の高さには、フッターも、前後の記事へのリンクも、一覧へ戻るリンクも含まれます。どれも「読む」対象ではありません。
測りたかったのは「本文をどこまで読んだか」なのに、分母は「ページをどこまで動かしたか」でした。名前は進捗でも、中身はスクロール量です。
分母を本文に変えた
基準を、ページ全体から本文の高さに変えました。
進捗 = (画面の下端 − 本文の開始位置) ÷ 本文の高さ
本文の末尾が画面の下端に来た時点で100%になります。読み終えたら100%という、当たり前の対応になりました。
ただし、これだけでは足りない
末尾への到達だけを条件にすると、別の問題が残ります。目次から末尾の見出しへ飛んだ場合も、一気に下まで送った場合も、読了になります。
「画面が下まで移動した」ことと「読み終えた」ことは、別のできごとです。
そこで条件を2つにしました。
| 条件 | これが無いと |
|---|---|
| 本文の末尾まで到達している | 開いて放置しただけで読了になる |
| 一定時間、実際に見ている | 下まで送っただけで読了になる |
時間の基準は読了目安の4分の1にしました。8分の記事なら2分です。速く読む人を弾かない程度に緩く、しかし一瞬で下まで送った操作は通らない、という線を狙っています。
「開いていた時間」ではなく「見ていた時間」
単純に開いてからの経過時間で測ると、タブを開いたまま放置した時間まで読書時間になります。それでは条件の意味がありません。
ブラウザには「このページがいま見えているか」を知る手段があります。見えている間だけ加算するようにしました。席を外して戻ってきた人が損をしません。
一度到達したことを、忘れないようにする
実装中に、条件が2つになったことで生まれる穴に気づきました。
2つの条件は、同時に満たされるとは限りません。先に末尾まで読み、そのあと上に戻って読み返している最中に時間の条件を満たすことがあります。
このとき現在位置だけを見ていると、「いま末尾にいないので読了ではない」と判定されます。一度は最後まで読んだという事実が、位置を戻した瞬間に消えてしまいます。
到達はできごとで、位置は状態です。できごとを状態から読み取ろうとすると、状態が変わったときに失われます。
「一度でも到達したか」を別に記録するようにしました。読み返しても消えません。
高さが変わることも見るようにした
もうひとつ。本文の高さは、読んでいる最中にも変わります。画像が後から読み込まれる、目次を開く、画面を回す。
元の実装は画面サイズが変わったときしか計算し直していませんでした。画像の読み込みでは、その通知は来ません。
要素の大きさが変わったこと自体を教えてくれる仕組みに変えました。高さがずれたまま古い値で判定し続けることが無くなります。
表示のほうも、記録に合わせた
最後に、細かいけれど気持ち悪い食い違いを直しました。
画面の「読了済み」という表示は、残り0%になった時点で出ていました。しかし実際に記録されるかどうかは、時間の条件も見ています。
つまり画面には読了済みと出ているのに、記事一覧には読了マークが付かない状態があり得ました。
表示のほうを、記録に従わせるようにしました。2つの真実を持たない、というだけの話です。
今回の学び
まず、割合で測るなら、分母が何かを疑うこと。今回は分母に、読む対象でないものが1割ぶん入っていました。式は正しく動いていて、測っているものが違っただけです。正しく計算された、意味の違う数字がいちばん見つけにくいと思います。
次に、推測で直さず、まず測ること。「95%は厳しすぎるから90%に下げよう」でも症状は消えたはずです。しかしそれは、フッターの高さが変わるたびに再発します。89.9%という数字を出したから、分母の問題だと分かりました。
そして、操作と、意味を区別すること。「下までスクロールした」は操作で、「読み終えた」は意味です。操作から意味を推し量るときは、必ずずれが出ます。ずれを埋める条件が要ります。
最後に、できごとは、状態から読み取らないこと。一度起きたことは、現在の状態を見ても分からなくなることがあります。起きた時点で記録しておけば、あとから位置が変わっても失われません。
関連記事

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

届かない問い合わせ
問い合わせフォームで決めることは3つあった。メールアドレスを載せるか、送信先が未設定のときフォームを出すか、失敗をどう伝えるか。共通していたのは「うまくいったように見えて、実は何も起きていない」状態をどう避けるかだった。
追従する目次と、画面の取り合い
PCで右側に追従させていた目次を、スマホでは上部に貼り付けた。ところが貼り付くものは画面を占め続けるため、本文を読む場所が足りなくなる。情報を消さずに縦を横へ並べ替えた話と、「飛んだ先がバーの裏に隠れる」という副作用への対処。