monolithic kernel

JavaScriptで一時的にスクロールを無効化する その2

前回とは異なる方法でスクロールを無効化する方法を探ってみました。今回はちゃんとIE (8以上) で動きます!

コード

コードはこちら。addCSSscrollBarWidth はそれぞれコメントで示したURLを参考にさせていただきました。

// http://seussnu.b.sourceforge.jp/?p=22
var addCSS = (function() {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
document.getElementsByTagName('head')[0].appendChild(style);
var stylesheet = document.styleSheets[0];
if (stylesheet.insertRule) {
return function(selector, property, value) {
stylesheet.insertRule(selector + '{' + property + ':' + value + ';}', stylesheet.cssRules.length);
};
}
else {
return function(selector, property, value) {
stylesheet.addRule(selector, property + ':' + value);
};
}
})();
// http://davidwalsh.name/detect-scrollbar-width
var scrollBarWidth = (function() {
var scrollDiv = document.createElement("div");
scrollDiv.style.width = '100px';
scrollDiv.style.height = '100px';
scrollDiv.style.overflow = 'scroll';
scrollDiv.style.position = 'absolute';
scrollDiv.style.top = '-9999px';
document.body.appendChild(scrollDiv);
var w = scrollDiv.offsetWidth - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);
return w;
})();
addCSS('.noscroll', 'position', 'relative');
addCSS('.noscroll', 'overflow', 'hidden');
addCSS('.noscroll', 'padding-right', scrollBarWidth + 'px');

使い方

使う時はbody要素のクラスにnoscrollを追加するとスクロールが無効化され、削除すると再びスクロールできるようになります。

var addClass = function(element, className) {
var classes = element.className.split(/\s+/);
var i, n;
for (i = 0, n = classes.length; i < n; ++i) {
if (classes[i] == className) {
return;
}
}
classes.push(className);
element.className = classes.join(' ');
};
var removeClass = function(element, className) {
var classes = element.className.split(/\s+/);
var result = [];
var i, n;
for (i = 0, n = classes.length; i < n; ++i) {
if (classes[i] != className) {
result.push(classes[i]);
}
}
element.className = result.join(' ');
};
addClass(document.body, 'noscroll');
removeClass(document.body, 'noscroll');

jQueryを使っているのであればクラスの操作は以下のようにできます。

$('body').addClass('noscroll');
$('body').removeClass('noscroll');

実現方法

overflow:hiddenをbody要素に適用してスクロールバーを消し、スクロールさせないようにしています。しかし、それだけだとスクロールバーが消えた分描画領域が広がってしまい、一瞬表示が崩れてしまうので、事前にスクロールバーの幅を計算しておき、その分だけbody要素のpadding-rightを設定して縮めることで崩れを防いでいます。

問題点

スクロールバーを消した分の幅はpaddingで埋めているのですが、これでは誤魔化せない場所もあります。例えば、CSSのMedia Queriesの条件に使うスクリーンサイズはスクロールバーの有無で変わってしまうので、スタイルが切り替わる境界付近でスクロールを禁止すると、レイアウトが変化して見苦しくなる可能性があります。

2013-05-26 追記

この挙動、仕様ではなくWebKitのバグだったみたいですね。Blinkでは修正されたようです。WebKitも早く修正されるといいですね。


Related articles