書くのが遅くなってしまいましたが、本記事はLIFULL Advent Calendar 2018の25日目の記事になります。
今年ももう終わりそうですが、全然Qiitaに記事を書けていません。
個人的には、Vue.jsやBigQueryなど初めて触れる技術もあり、なかなか刺激が多い一年だったんですが、こういう場所でアウトプットするほどの内容でもなく。
毎年クリスマスにAdvent Calendarでなにか書く星のもとに生まれたので、先日へーっとなった件を書いておきます。
##三行で
-
jQuery.scrollTop
にはfunction
を引数で渡すことができる - 公式ドキュメントを見てもよくわからん
- ソース頑張って追ったらわかった
##はじめに
デバイス問わず、サイト内でユーザーのアクションに応じて画面をスクロールすることがあると思います。
jQueryを使って下記のような感じです。
// hoge要素をクリックしたら、fuga要素のトップまでスクロール
$('#hoge').on('click', function () {
// fuga要素のwindow内での高さを取得
var height = $('#fuga').offset().top;
$(window).scrollTop(height);
});
最近ソースレビューをしていて、あれ?となったのですが、scrollTop
の引数にfunction
を渡しても動きます。
$('#hoge').on('click', function () {
$(window).scrollTop(getFugaTop);
});
// fuga要素のwindow内での高さを取得する
var getFugaTop = function () {
return $('#fuga').offset().top;
};
これが、下記のように関数実行していたら全然違和感なかったのですが、上記の通りfunction
自体が渡せるのです。
// わかる
$(window).scrollTop(getFugaTop());
// え??? でも動く
$(window).scrollTop(getFugaTop);
##公式ドキュメントを見てみる
https://api.jquery.com/scrollTop/
.scrollTop( value )
value
Type: Number
A number indicating the new position to set the scroll bar to.
だよね、数値渡すものだと思ってた。
わからぬ。
##ソースを見てみる
###offset.js
https://github.com/jquery/jquery/blob/master/src/offset.js#L199
長いけど大事なところなので全部引っ張ってきた。
// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
var top = "pageYOffset" === prop;
jQuery.fn[ method ] = function( val ) {
return access( this, function( elem, method, val ) {
// Coalesce documents and windows
var win;
if ( isWindow( elem ) ) {
win = elem;
} else if ( elem.nodeType === 9 ) {
win = elem.defaultView;
}
if ( val === undefined ) {
return win ? win[ prop ] : elem[ method ];
}
if ( win ) {
win.scrollTo(
!top ? val : win.pageXOffset,
top ? val : win.pageYOffset
);
} else {
elem[ method ] = val;
}
}, method, val, arguments.length );
};
} );
どうやらscrollTop
,scrollLeft
はなかでaccess
という関数を使いながら、window.scrollTo
に委譲しているらしい。
access
ってなにものだ?
###access.js
ファイル全部になっちゃうので要所だけ。
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
len = elems.length,
bulk = key == null;
これのfn
が上の関数ね。value
が呼び出し元の引数(今回の大元の引数)か。
if ( !isFunction( value ) ) {
raw = true;
}
お?value
がfunctionだとraw
はいじられず、今回だとundefined
らしい。
if ( fn ) {
for ( ; i < len; i++ ) {
fn(
elems[ i ], key, raw ?
value :
value.call( elems[ i ], i, fn( elems[ i ], key ) )
);
}
}
scrollTop
側のfn
に戻って、第三引数のval
がvalue.call
になるんか!
しかもご親切にelement
とかcallbackとしてfn
渡してくれてたりでいろいろやれそう。
なんでこれがドキュメントに書いてないんだ??
##最後に
これって多分scrollTop
とかに限った話じゃなさそうですよね。
access
経由で実装されているjQueryのメソッドなら同じことができそう。
https://github.com/jquery/jquery/search?l=JavaScript&q=access
例えば、、
https://github.com/jquery/jquery/blob/master/src/dimensions.js
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
なんだかAdvent Calendarやクリスマスにふさわしい話でもないですが、最近へーっとなったお話でした。