背景
offsetTopとclientTopの使い分けについてです。
両者の違いはoffsetTopがoffsetParentからの相対値で、clientTopはブラウザの左上からの相対位置です。
offsetParentと言われても意味不明ですが、ここではposition: absoluteなHTML要素の一番近い先祖のposition: relativeな要素です。次のようなイメージです。
<div id="offsetParent" style="position: absolute; witdth: 100px; height: 100px"> <div class="offsetChild" style="position: relative; top: 10px; left: 10px; witdth: 100px; height: 100px"></div> <div class="offsetChild" style="position: relative; top: 20px; left: 40px; witdth: 100px; height: 100px"></div> </div>
一つ目のoffsetChildのoffsetTopは常に10pxです。
clientTopは縦スクロールの位置によって変わります。
offsetParentの左上がちょうど、ブラウザの左上と一致した時、offsetTopとclientTopは同じ値になります。
課題
上の例のoffsetChildにtop, leftを設定して良い感じの位置に表示するプログラムを書いています。
今までHTML要素の位置を設定するために、offsetTopとしての値しか扱っていませんでした。
変数名やプロパティ名もシンプルにtopとしていました。
ここ最近、First Meaningful Paint の最適化に凝っています。
First Meaningful Paint のためには、これから表示するdivが、ブラウザで表示されている領域の中にあるか外にあるかが重要です。
ブラウザでdivが表示されているか判定する - @ledsun blog で、考えたようにこの判定にはclientTopを使います*1。
ここで困ったのがtopという名前だとoffsetTopを意味しているのかclientTopを意味しているのかわからないことです。
そして悩みはじめた時点では、offsetTopという既知の最高の名前があることを忘れていました。
解決
リファクタリングしながら3時間半考えて「いままでのtopが表しているのはoffsetTopだ」と天啓を得ました。
あとは1時間くらいゴリゴリっと名前を変えるリファクタリングして回って、15分くらいclientTopを取るAPIを追加して、10分でclientTopを使った新機能*2を実装しました。
2017年頃は、じっくり考えてから実装するスタイルでした。 いまはリファクタリングしながら考えるスタイルに変わりました。 「道がみつかるまで手を動かせなかった」から「手を動かせば道が見つかる」を経験的に学習したようです。 しかも、リファクタリングした結果、読みやすいソースコードまで手に入るオマケ付きです。