直したのに、直らない
自分のサイトを手元の端末で見ていて、気づきました。 ページを下へスクロールしていくと、途中から右側に余白ができる。 指を離すと戻ることもあれば、戻らないこともある。
CSS の問題だろうと見当をつけて、思い当たるところを直しました。直りました。 翌日また出ました。別のところを直しました。直りました。また出ました。
これを5回ほど繰り返して、ようやく気づきます。 「直った」という判定が、当てにならない。
個々の CSS 的な原因と対処は別の3本にまとめました。ここでは技術の中身ではなく、どうやって「直った」を判定するかだけを扱います。
スマートフォンだけで、追いかけた
症状が出るのはスマートフォンです。だから確認もスマートフォンで行いました。 手元にあったのは、この2つだけです。
| 手段 | できること |
|---|---|
| テキストエディタのプレビュー | コンソールが使える。JS を実行して要素を調べられる |
| サーバーにアップロードして表示 | 本番と同じ状態を目で見る |
いま振り返ると、この構成に決定的な穴がありました。 ただし、その穴に気づくのはかなり後になってからです。
測る道具を、自分で間違える
推測で直すのをやめて、計測することにしました。 画面幅を超えている要素を探す短いスクリプトを書き、コンソールで実行します。
document.querySelectorAll('*').forEach(el => { const r = el.getBoundingClientRect(); if (r.right > innerWidth) hits.push(el); });
3件見つかりました。すべて span です。直しました。
結果は変わりませんでした。
span は、横スクロールできる箱の中身でした。
枠からはみ出していますが、それはスクロールして読むためのもので、
ページ幅には影響しません。
祖先にスクロール容器があるかを見ていなかったのが原因です。
// 祖先のどこかで横がクリップされていれば、ページ幅には響かない function contained(el){ for (let p = el.parentElement; p && p !== document.documentElement; p = p.parentElement) if (getComputedStyle(p).overflowX !== 'visible') return true; return false; }
直したスクリプトを実行すると、今度は 0件。 ところが症状は出ています。
効いたのか、たまたまか
対処を入れては確認する、を繰り返していました。 その判定はこうです——ページを開いて、余白が出なければ直った。
これで問題ない、と思っていました。実際、 多くの不具合はこの判定で足ります。原因が1つで、常に同じ結果が出るなら。
3層に重なっていた
最終的に分かった原因は、3つありました。
| 層 | 内容 | 現れ方 |
|---|---|---|
| ① CSS の不備 | 縮む余地が無い要素があった | 常に寄与 |
| ② 初回の計算 | 読み込み直後の幅が確定しきらない | 読み込み直後だけ |
| ③ 空の広告枠 | 配信されなかった枠が幅を持って残る | 確率的 |
問題は③です。広告が配信されるかどうかはこちらでは決められません。 3箇所のうち全部埋まることもあれば、1つ空くこともある。 同じページを開いても、毎回条件が違う。
だから「直った」に見えた
①を直した状態で開くと、こうなります。
| 回 | 空いた枠 | はみ出し量 | 見え方 |
|---|---|---|---|
| 1 | 1個 | 52 px | 出る |
| 2 | 0個 | 12 px | 出ない |
| 3 | 0個 | 12 px | 出ない |
| 4 | 1個 | 52 px | 出る |
私が5回繰り返したのは、これでした。 効いた対処と、たまたま出なかった回を、区別できていなかったのです。
判定が壊れるところを、見る
3つの原因を個別に「直した」状態にして、 20回ページを開いたときの結果を並べます。 広告の配信率も変えられます。
①だけを直した状態にして、何度か引き直してみてください。 連続して緑が並ぶことがあります。そこで確認をやめれば、「直った」と判定します。 直っていないのに。
規則性が、見つかる
転機は、対処を思いつかなくなってから来ました。 ただリロードを繰り返して眺めていたら、広告の出方と症状が対応していることに気づきます。
| 広告の状態 | 症状 |
|---|---|
| 3カ所すべて表示された | 出ない |
| 2カ所だけ表示(1カ所が空白) | 出る |
| 3カ所すべて削除した | 出ない |
3つ目が効きました。広告があるから悪いのではない。 全部消しても症状は出ないのですから。 埋まらなかった枠だけが問題でした。
見ていたものが、違った
原因が分かってから、§1 の構成に戻ります。 確認手段は2つありました。そのどちらにも、広告が無かったのです。
| 環境 | 広告 | 症状 |
|---|---|---|
| エディタのプレビュー | 配信されない | 出ない |
| サーバーにアップロード | 配信される | 出る |
コンソールが使えるのはプレビューだけ。つまり 計測できる環境には、原因が存在しなかった。 アップロードした先では症状が見えるけれど、そこでは細かく調べられない。
ただし、この差は道具になる
真因が分かってから、対処を外す
③を潰したあと、それまでに入れた対処を1つずつ検証しました。 もう要らないものが混ざっているはずだからです。
| 入れた対処 | 判定 | 理由 |
|---|---|---|
| 未配信の広告枠を畳む | 必須 | 真因 |
| 背景の描画範囲を限る | 必要 | 別の症状に効いていた |
| 要素に縮む余地を与える | 必要 | 実在した不備 |
| 広告枠の高さを予約する | 必要 | 確定までの揺れを抑える |
| ページ全体のはみ出しを隠す | 保険 | 無くても平気だが残す |
| 合成レイヤーへの昇格 | 不要 | 常時メモリを使う |
| 読み込み後の再計算 | 不要 | 毎回走る。①で解決済み |
2つ外せました。どちらも入れたときは効いたように見えたものです。
測れる状態を、先に作る
振り返ると、順序が逆でした。 直し方を考える前に、「直った」をどう判定するかを決めるべきだった。
1回開いて出なければ直った——この判定は、 原因が1つで、結果が毎回同じという前提の上に立っています。 その前提が崩れているのに気づかず、判定だけを使い続けたのが今回でした。
この記事のまとめ
- 原因が複数あると、部分的な対処が効いたり効かなかったりする。判定が成り立たなくなる。
- とくに確率的に現れる原因が混ざると、同じ状態でも結果が変わる。
- 2回続けて出なければ「直った」と判定してしまう。そこで確認をやめるのが危ない。
- 症状の有無だけを見ても分からない。何と対応して変わるかを見ると規則性が出る。
- 計測の道具も間違える。0件という結果が正しいこともある——寸法を持たない原因なら。
- 確認環境と本番環境の差を、先に確かめる。同じものを見ている保証は無い。
- その差は比較の材料にもなる。片方だけで出るなら、片方にしか無いものを疑う。
- 真因が分かったら、途中の対処を外す。効いて見えただけのものが混ざっている。