jScrollPaneとページ内リンク

スクロールバーのデザインを自由に変えられるjQueryプラグイン「jScrollPane」

iframeにこちらのプラグインでオリジナルデザインのスクロールバーを設置して、
且つ、
iframe内を移動するページ内リンクを設置したい
という試行錯誤のお話です。
サンプルはこちら。

jScrollPaneの設置方法は割愛しますが、
iframeには、読み込む側のページのほう、まるごとに適用させるみたいです。
配布サイトのコードはこんなかんじ。(抜粋です)

$(function()
{
	var win = $(window);
	// Full body scroll
	var isResizing = false;
	win.bind(
		'resize',
		function()
		{
			if (!isResizing) {
				isResizing = true;
				var container = $('#full-page-container');
				// Temporarily make the container tiny so it doesn't influence the
				// calculation of the size of the document
				container.css(
					{
						'width': 1,
						'height': 1
					}
				);
				// Now make it the size of the window...
				container.css(
					{
						'width': win.width(),
						'height': win.height()
					}
				);
				isResizing = false;
				container.jScrollPane(
					{
						'showArrows': true
					}
				);
			}
		}
	).trigger('resize');

	// Workaround for known Opera issue which breaks demo (see
	// http://jscrollpane.kelvinluck.com/known_issues.html#opera-scrollbar )
	$('body').css('overflow', 'hidden');

	// IE calculates the width incorrectly first time round (it
	// doesn't count the space used by the native scrollbar) so
	// we re-trigger if necessary.
	if ($('#full-page-container').width() != win.width()) {
		win.trigger('resize');
	}

	// Internal scrollpanes
	$('.scroll-pane').jScrollPane({showArrows: true});
});

この場合は#full-page-containerというボックスでbody以下をまるごと括ってるようです。

さて、プラグインをこうして実装したところで、ページ内リンクは動きません。
jScrollPaneが、
bodyと、プラグインを適用させたボックスを幅・高さを取得のうえでoverflow:hiddenしているせいかと思います。
で、
.jspContainer(position:relative)と
.jspPane(position:absolute)
を追加して、
.jspPaneのtopを動かしてスクロールさせているみたいです。
※v2.0.19を参照しています。クラス名はバージョンによって違うかもしれません。

ということは、ページ内リンクは移動先の位置を取得して、.jspPaneのtopを動かしてしまえば、無理やりですがなんとかなりそうです。
よくあるスムーススクロールのコードと、基本的には一緒で大丈夫なんじゃないでしょうか。

$(function(){
	$('a[href^=#]').on("click",function(){
		var href= $(this).attr("href"); //hrefの値を取得
		var position = $(href).offset().top;	//上からの位置を取得
		$("html, body").animate({scrollTop:position}, 500);	//html、bodyを取得した位置までアニメーション
		return false;	//通常リンクの動作を無効
	});
});

と、思ったんですが
offset().topで取った位置がなんだかおかしい。
クリックイベントの外でoffset().topするとまともそうな数字が出るんだけど……
jScrollPaneをちゃんと読んで理解できていないんですが
クリックイベントの中でoffset().topを取ると、どうやら表示中の画面端からの位置を取っているような??
bodyにoverflow:hiddenがかかってるから???

ということで、offset().topでなくて、position().topにしてみました。
position()は、position:static以外の直近の親からの位置を取るようなので、すべてのケースを解決はできなさそうですが
今回の場合は幸いcssにposition指定もなかったので……
これで

$('a[href^=#]').on("click",function(){
	var href= $(this).attr("href");
	var position = $(href).position().top;
	$(".jspPane").animate({'top': - position},500);	 //.jspPaneのtopを取得位置ぶん上げる	
	return false;				
});

できた!

ちなみに、これだけだとスクロールバーのドラッグ部分がついてこないので、そっち(.jspDrag)も一緒に動かします。
.jspPaneが中身の要素、.jspContainerが表示領域になっているので、上下矢印があればそのぶん考慮して割合出してあげればよさそうです。

$('a[href^=#]').on("click",function(){
	var href= $(this).attr("href");
	var position = $(href).position().top;
	var dragPer = $(".jspPane").height() / $(".jspContainer").height(); //中身の要素と表示領域の割合算出
	var dragPos = position / dragPer ; //リンク先位置を上の割合でスクロールバーのドラッグ部分の位置を出す
	$(".jspPane").animate({'top': - position},500);
	$(".jspDrag").animate({'top':dragPos},500); //スクロールバーのドラッグ部分も動かす
	return false;		
});

コメントする