HTMLElement を拡張して手軽に scroll rate を取得できるようにしよう

phi phi on javascript

要素のスクロール値を取得するには, 大抵の場合 scrollTop プロパティを参照すると思います.

単純に値を取得するだけならこれで良いのですが, スクロール値が全体のx%に来たらAを実行する といった場合は
ちょっとした計算が必要になります.

その計算を省略できるようにしてみたので 備忘録がてら載せておきます.

Runstant Demo

枠の中をスクロールすると色が変わるのが分かるかと思います.

Code

html

<body>  
  <h1>JavaScript で要素のスクロール値を取得して背景色に反映させてみよう</h1>
  <div class='wrap'>
    <h2>スクロールしてね♪</h2>
    <div class='pad'></div>
  </div>
</body>  

css

.wrap {
  box-shadow: 0px 0px 4px #aaa;
  height: 200px;
  width: auto;
  overflow: auto;
}

.pad {
  height: 2000px;
}

javascript

Object.defineProperty(HTMLElement.prototype, 'scrollTopRate', {  
  get: function() {
    var maxScrollValue = this.scrollHeight - this.offsetHeight;
    return (this.scrollTop/maxScrollValue);
  }
});

window.onload = function() {  
  document.querySelector('.wrap').onscroll = function() {
    // スクロールレートを取得
    var rate = this.scrollTopRate;
    // hsl 値を作成
    var color = 'hsl(' + (rate*360) + ', 90%, 70%)';
    // 背景色を変更
    this.style.backgroundColor = color;
  };
};

Tips

scroll 値 rate の計算方法

scroll 値の計算は前回書いた『JavaScript で要素のスクロール値を取得して背景色に反映させてみよう | phiary』でも紹介した方法と同じです.

var v = スクロール値/(要素全体の高さ - 要素の高さ)  

コードにするとこんな感じですね.

var maxScrollValue = this.scrollHeight - this.offsetHeight;  
return (this.scrollTop/maxScrollValue);  

defineProperty による既存クラスの拡張

Object.defineProperty で既存クラスに対してメソッドを追加することができます.

下記のような形で指定します.

Object.defineProperty(拡張したいクラス, 追加するプロパティor メソッド名, デスクリプタ);  

今回は, HTMLElementscrollTopRate というメソッドを追加しています.

Object.defineProperty(HTMLElement.prototype, 'scrollTopRate', {  
  get: function() {
    var maxScrollValue = this.scrollHeight - this.offsetHeight;
    return (this.scrollTop/maxScrollValue);
  }
});

これで全てのHTML要素で 要素.scrollTopRate とするだけで 今スクロールがどの位置かを rate(0.0~1.0) で取得することができます.

Core クラス拡張による危険性

今回 HTMLElement を拡張しているのですが, 賛否両論あると思います. Core Class を拡張するのは名前のバッティングリスクがあるので,
あまり推奨しない方もたくさんいます.

とはいえ, 使い勝手やコードの可読性を考えるとどう考えても拡張した方が良い場合もあります.

以前は, よく for in で混じってくるから良くない という批判があったのですが, defineProperty が出てきてからは enumerable でオンオフできるようになったので気にする必要はありません.

名前はしっかりと考慮する必要がありますが, 今後その名前で 何か実装される予定がなければ拡張するのもありかなと思います.