作成したJavaScriptの問題点
前回、JavaScriptでスムーススクロールを実装しましたが、実際にWebサイトで使うにはいくつか問題があります。
そのひとつとして、使っている変数が、他のJavaScriptに影響をしてしまう問題があります。
他のJavaScriptで同じ名前の変数や関数があると、読み込む順番によっては自分の変数の値が他のJavaScriptの処理で意図しない値に書き換わったり、他で使われている変数を上書きしてしまうことがあります。
自分だけがWebサイトを構築して、完全に読み込むJavaScriptを把握していれば回避できるかも知れません。
しかし、人間とは間違うものです。うっかり対応を忘れて変数名が重なってしまい、エラーが発生して対応に頭を悩ますことになるでしょう。
また、複数人でWebサイトに関わることがあれば、配布されているJavaScriptを使うこともあります。そうなればより複雑な状況になって手に負えなくなります。
そこで、カプセル化です。
データと関数を一つのコンポーネント (例えば、クラス) に閉じ込め、そのコンポーネントへのアクセスを制御することにより、そのオブジェクトを “ブラックボックス” にすることです。
https://developer.mozilla.org/ja/docs/Glossary/Encapsulation
変数や関数を閉じ込めて(カプセル化)、他に影響が出ないようにする手法を使うと、他のJavaScriptに影響されず、また影響することを防ぐことができます。
そのカプセル化を実現するために、即時関数を使ってみましょう。
即時関数でカプセル化
即時関数は、正式には「即時実行関数式」と呼ばれ、定義されるとすぐに実行されるJavaScriptの関数のことです。
スムーススクロールは指定した要素の中にあるリンクを探して、クリックされたらスムースにページをスクロールさせるという機能のため、JavaScriptが読み込まれたタイミングで実行された方が良いですね。
関数の中でvarによる変数宣言すれば、その関数の中だけのローカル変数となりますから、他のJavaScriptの変数に影響を与えることも、受けることもありません。
では、この即時関数を使って、スムーススクロールを改修してみましょう。
スムーススクロールを即時関数に改修
まずは、おさらいです。
前回作成したJavaScriptコードは以下のとおりです。
var $link;
var href;
var target;
var position;
$link = $('#gNav').find('a');
$link.on('click', function () {
href = $(this).attr('href').replace(/.*(#.*)/g, '$1');
if ( href === '#' || href === '' ) {
target = $('html');
} else {
target = $(href);
}
position = target.offset().top;
$('body, html').animate({scrollTop: position}, 800, 'swing');
return false;
});
改めて見ると、変数がグローバルで宣言されていることが分かります。
また、その名前もhref, target, positionなど、よく使われる英単語なので、他のJavaScriptに影響が出てしまいそうです。
このコードをカプセル化して、他のJavaScriptに影響が出ないように改修しましょう。
ページの読み込みが完了した後でないと、JavaScriptがスムーススクロールを設定する要素を見つけられず、リンクをクリックしてもスムースにスクロールしません。
jQueryにはDOMの読み込みが完了した後に実行するための記述がありますので、それを使います。
$(function(){
// ここに処理を書く
});
続いて即時関数の構文を書きます。
$(function(){
(function(){
// ここに処理を書く
})();
});
$(function(){
(function(){
var $link;
var href;
var target;
var position;
// 中略
})();
});
この改修で、スムーススクロールで使っていた変数はプライベート変数となり、他でhrefやtargetなどの変数が使われても影響しなくなります。
まとめ
今回改修したJavaScriptは以下のようになります。
$(function(){
(function() {
var $link;
var href;
var target;
var position;
$link = $('#gNav').find('a');
$link.on('click', function () {
href = $(this).attr('href').replace(/.*(#.*)/g, '$1');
if ( href === '#' || href === '' ) {
target = $('html');
} else {
target = $(href);
}
position = target.offset().top;
$('body, html').animate({scrollTop: position}, 800, 'swing');
return false;
});
})();
});
このコードでは問題点や改良点が残っていますが、まずは変数のグローバル汚染を回避するための手法を書いてみました。
自分が書いたコードがうっかり他のJavaScriptに影響して、バグを生み出して解決に時間を取られるなんてことがないように、きちんとしたコードを書けるように気をつけたいものです。