Hatena::ブログ(Diary)

Cyokodog :: Diary

こちらのブログは更新を停止しております。最新の記事は以下ブログをご覧ください。
www.cyokodog.net

January 12, 2009

jQuery でブラウザの表示領域に対するサイズや位置情報を取得してみる

更新履歴

2010-01-21
本エントリの内容も含めた最新の情報は下記エントリをご参照ください。

f:id:cyokodog:20090113031331p:image

前回前々回のエントリではボックス要素を例にサイズや位置、スクロール量などの求め方について書きましたが、実際のプラグインの実装においてこれらの情報が必要になるのは、ブラウザの表示領域に対してということが多いかと思います。

具体的にはツールチップなどの機能で、画面の端の要素を hover した時、ポップアップブラウザの表示領域内に収まるように表示位置を調整するような場合に、ブラウザの表示領域のサイズやスクロール量などが必要になります。

jQueryブラウザの表示領域をつかむ方法

ブラウザの表示領域を jQuery でつかむには、以下のような記述でできそうです。

$('html')
$(window)
$(document)

DOM ノード上の先祖要素を

$('div.popup').parents()[$('div.popup').size()-1]

というふうに参照すると、html 要素が返ってくるので、プラグインとしての汎用性(擬似フレームの対応など)や保守性(コードの共通化)を考えると、$('html') のみで、諸々の情報が全て取得できたら理想的かと思われます。


主要ブラウザでサイズ・位置情報を取得してみる

IE6,IE7,IE8 Beta2,Firefox3,Safari3,Opera9.5 で、サイズ・位置情報を取得してみました。

確認用ページ

こちら

スクリーンショット
IE6(XP)

f:id:cyokodog:20090109164833p:image:medium

IE7(Vista)

f:id:cyokodog:20090109125437p:image:medium

IE8 Beta2(IETester)

f:id:cyokodog:20090109125438p:image:medium

Firefox3

f:id:cyokodog:20090109125435p:image:medium

Safari3

f:id:cyokodog:20090109125439p:image:medium

Opera9.5

f:id:cyokodog:20090109125440p:image:medium

ブラウザ間でかなり差異がでました。

汎用的に使うには独自拡張したラッパー・メソッドを書く必要がありそうです。

$(window)、$(document) ではほとんどの実行結果が、実行時エラーもしくは undefined でしたので、$('html') をベースに拡張し、$('html') では取得できない部分を $(window)、$(document) で補完するのがいいかと思います。


位置情報の取得結果を比較してみる

scrollTop()、scrollLeft() メソッド

まずは重要そうな スクロール量の取得から確認してみます。

$('html')$(window)$(document)
IE6スクロール量スクロール量スクロール量
IE7スクロール量スクロール量スクロール量
Firefox3スクロール量スクロール量スクロール量
Safari3常に 0スクロール量スクロール量
Opera9.5スクロール量スクロール量スクロール量

Safari の $j('html') が、常に 0 になってしまいますので、以下のようにクロスブラウザ対応すればいいかと思います。

$.fn.exScrollTop = function(){
    return this.attr('tagName')=='HTML' ? $(window).scrollTop() : this.scrollTop();
}

position() メソッド

position() メソッドは CSS 的な意味での位置情報を返してくれます。詳細は前回のエントリを参照ください。

意図的にブラウザ表示領域に対し、このメソッドを使用するケースは無いかと思いますが、プラグインの実装などで、コンテナ要素を自動判別し且つ、コンテナ要素の表示位置を求めるといった事も想定できるので確認してみます。

$('html')$(window)$(document)
IE6スクロール量 - 200
IE7スクロール量00
IE8000
Firefox3errorerrorerror
Safari3errorerrorerror
Opera9.5errorerrorerror

IE 以外では実行時エラーになりました。

IE7 の $('html') では scrollTop() などで求められるスクロール量と同じ値になりました。

IE6 の $('html') ではデフォルトhtml に border-width:2px が設定されており、スクロール量からこの幅を引いた値が返されるようです。XP + IE6 で確認しました。

ちなみに Vista + IE Tester の IE6 で確認すると -2px はされないようです。(以下スクリーンショット参照)

f:id:cyokodog:20090109125436p:image:medium

クロスブラウザ対応は常に 0 を返すようにすればいいかと思います。

$j.fn.exPosition = function(){
	var o=this;
	return o.measur(function(){
		if(o.attr('tagName')=='HTML'||o[0]==window||o[0]==document)
			return {top:0,left:0};
		else{
			var pos=o.position();
			var container = o.exContainer();
			if(container.attr('tagName')=='HTML')return pos;
			return {
				top:pos.top+container.exScrollTop(),
				left:pos.left+container.exScrollLeft()
			}
		}
	})
}

offset() メソッド
$('html')$(window)$(document)
IE6スクロール量 - 200
IE7スクロール量00
IE8000
Firefox30errorerror
Safari30errorerror
Opera9.50errorerror

position() メソッドとほぼ同じ結果ですが、IE 以外でも $('html') でエラーがでなくなりました。

こちらもクロスブラウザ対応は常に 0 を返すようにすればいいかと思います。

$j.fn.exOffset = function(){
    return this.attr('tagName')=='HTML'||this[0]==window||this[0]==document ?
        {top:0,left:0} : this.offset();
}

サイズの取得結果を比較してみる

どの範囲のサイズを取得してるのか分かりづらいので、取得箇所の図と対応させてまとめました。

f:id:cyokodog:20090112174707p:image

ちなみに html 要素の border、padding 指定は省きました。(デフォルトのまま)

最初は指定して検証してたのですが、ブラウザ間の差異がはげしく検証がしんどっかたのでやめました。

クロスブラウザの対応については以下についてのみ考えてみます。

    • offsetHeight,offsetWidth
    • scrollHeight,scrollWidth
    • clientHeight,clientWidth

height(),width() メソッド
$('html')$(window)$(document)
IE6h2:w2h1:w1h3:w3
IE7h1:w1h1:w1h3:w3a
IE8h3:w1h2:w2h3:w1
Firefox3h3:w1h1:w1h3:w3a
Safari3h3:w1h2:w2h3:w3a
Opera9.5h3:w1h3b:w3bh3:w3a

Opera9.5 の w1 と w3b は更にスクロールバーの幅を引いた値になってます。

かなりばらばらな結果になりました。

もはやのどのブラウザが正しい振る舞いをしてるのか分かりません。

とりあえず $('html')、$(window) で height() や width()をそのまま使うのはクロスブラウザ的に危険だったということが分かりました。


outerHeight(),outerWidth() メソッド
$('html')$(window)$(document)
IE6h2:w2h1:w1h3:w3
IE7h1:w1h1:w1h3:w3a
IE8h3:w1h2:w2h3:w1
Firefox3h3:w1errorerror
Safari3h3:w1errorerror
Opera9.5h3:w1errorerror

IE 以外のブラウザで、$(window),$(document)が実行時エラーになる事意外は、height(),width() メソッドと同様の結果になりました。


innerHeight(),innerWidth() メソッド

outerHeight(),outerWidth() メソッドと同様の結果となりました。

html 要素に border を設定するとその幅分マイナスされた値が取得できるようです。


attr 系メソッド

$(window),$(document)だと全てのブラウザで実行時エラーになります。

$('html') の結果のみを表にまとめます。

offsetHeight系scrollHeight系clientHeight系
IE6h2:w2h2:w3h1:w1
IE7h1:w1h3:w3ah1:w1
IE8h3:w1h3:w1h2:w2
Firefox3h3:w1h3:w3ah1:w1
Safari3h3:w1h3:w3ah1:w1
Opera9.5h3:w1h3:w3ah1:w1

クロスブラウザ対応は、以下のような値が求まるようにしてみます。

offsetHeighth2
offsetWidthw2
scrollHeighth3
scrollWidthw3a(IE6 のみ w3)
clientHeighth1
clientWidthw1
//スクロールバー有無、サイズ取得
var isDisplayScrollBar=function(target,key){
	var val=target.css('overflow-'+key)
	if(val=='scroll')return true;
	if(val=='hidden')return false;
	if(val=='auto'||target.attr('tagName')=='HTML'){
		var method=(key=='y'?'Height':'Width');
		return target['exClient'+method]()< target['exScroll'+method]()
	}
	return false
}
$j.fn.isDisplayScrollBar=function(key){
	return isDisplayScrollBar(this,key)
}
var scrollBarWidth=function(target){
	var w=jQuery.browser.msie?16:jQuery.browser.safari?15:17;
	return {
		x : target.isDisplayScrollBar('x')?w:0,
		y : target.isDisplayScrollBar('y')?w:0
	}
}
$j.fn.scrollBarWidth=function(){
	return scrollBarWidth(this)
}
//非表示要素でも採寸できるようにする
$j.fn.measur=function(f){
	var o=this,ret;
	var hide=o.is(":hidden");
	if(hide)o.show();
	ret=f();	
	if(hide)this.hide();
	return ret;
}
//こっからがメイン
$j.fn.exClientHeight = function(){
	var o=(this[0]==window?$j('html'):this)
	return o.attr('clientHeight');
}
$j.fn.exClientWidth = function(){
	var o=(this[0]==window?$j('html'):this)
	var s=o.measur(function(){return o.attr('clientWidth')});
	if(o.attr('tagName')!='HTML')return s;
	if(jQuery.browser.msie && jQuery.browser.version==7)
		return o.width();
	return s;
}
$j.fn.exScrollHeight = function(){
	var o=this;
	var s=Math.max(
		o.measur(function(){return o.attr('scrollHeight')}),
		o.exClientHeight());
	if(o.attr('tagName')!='HTML')return s;
	if(jQuery.browser.msie && jQuery.browser.version==6)
		return $j(document).height();
	return s;
}
$j.fn.exScrollWidth = function(){
	var o=this;
	return Math.max(
		o.measur(function(){return o.attr('scrollWidth')}),
		o.exClientWidth());
}
$j.fn.exOffsetHeight = function(){
	var o=this;
	var s=o.measur(function(){return o.attr('offsetHeight')});
	if(o.attr('tagName')!='HTML')return s;
	if(jQuery.browser.msie && jQuery.browser.version==6)
		return s;
	return o.exClientHeight()+o.scrollBarWidth().x

}
$j.fn.exOffsetWidth = function(){
	var o=this;
	var s=o.measur(function(){return o.attr('offsetWidth')});
	if(o.attr('tagName')!='HTML')return s;
	if(jQuery.browser.msie && jQuery.browser.version==6)
		return s;
	return o.exClientWidth()+o.scrollBarWidth().y
}

$j.fn.scrollBarWidth でスクロールバーのサイズを取得してるのは、offsetWidth(offsetHeight)を求めるためで、clientHeight(clientWidth) にスクロールバーのサイズを加算した値を返しています。

実質、ブラウザの表示領域の情報としては、clientHeight(clientWidth)、scrollHeight(scrollWidth)があれば十分な気もしますが、プラグインの汎用処理で、$('html')で offsetHeight(offsetWidth)メソッドが動いてしまうケースがあるかもしれないので一応書いてみました。

attr系メソッドだと採寸対象要素が非表示だとサイズが取得できないので、measurメソッドで一時的に対象要素を表示状態にしています。

あと、w3 (通常 scrollWidth でとれる値) の幅が取れなかったので IE8 の対応は断念しました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元