jQuery.exFixed.js を機能拡張しました

以前、exFixed という jQuery プラグインを作成しました。IE6 で position:fixed を実現するプラグインで、特徴として

  • IE6 でもガタつかない position:fixed が可能
  • クロスブラウザで fixed 要素に対し、位置移動、リサイズ等の動的な変更が可能(素のanimate メソッド等も併用可)

といった事が挙げられます。

今回、このプラグインをご利用下さった方々からのフィードバックを元に、機能拡張と不具合修正を行いました。

機能拡張

これまでは IE6 でもモダンブラウザの position:fixed と同レベルの扱いが出来ることを目指してましたが、今回は以下のような position:fixed 自体の機能拡張を行いました。

baseNode パラメータ
fixed 要素の配置上の基準位置となる要素を指定することができます
baseX , baseY パラメータ
垂直または水平の単一方向にのみ baseNode パラメータ による基準位置指定を有効にすることができます
fixedX , fixedY パラメータ
垂直または水平の単一方向にのみ position:fixed を適用することができます
resize メソッド
動的な fixed 要素のリサイズ / 位置移動処理を簡単に記述することができます
不具合修正
  • IE6 で body 要素に background-image を設定した状態で exFixed を使用すると、background-image の設定がクリアされてしまう不具合を修正
  • IE8 の標準モードで exFixed を使用すると実行時エラーが発生する不具合を修正
  • fixed 要素の一部あるいは全体がブラウザの表示領域から隠れた状態で、スクロールバーを動かすと無限スクロール状態になる不具合を修正
  • IE6 以外のブラウザで、スクロールされた状態で更新ボタンを押すと、fixed 要素が非表示になってしまう不具合を修正

新たに追加された機能

baseNode パラメータ

通常 position:fixed された要素の配置位置は、ブラウザの表示領域の四隅のいずれかが基準位置になりますが、baseNode パラメータに要素を指定すると、指定した要素の配置位置が fixed 要素の基準位置となります。

上図のようにコンテナ要素の右上に fixed 要素を配置したい場合は、baseNode パラメータにコンテナ要素を指定し、top , right の値を 0 にします。

$('#target').exFixed({
    baseNode : '#container',
    top : 0,
    right : 0
});

Demo

ちなみにコンテナ要素に position:relative 、fixed 要素に position:absolute を指定すれば、コンテナ要素からの相対的な配置は可能ですが、スクロール時は(当然ですが)固定表示されません。

ブラウザリサイズ時の配置の追従

サンプルのようにコンテナ要素の幅が固定長で両サイドが margin:auto の場合、ブラウザリサイズ時(コンテナ要素は常にセンタリングされた状態を保とうとするので)コンテナ要素の配置位置が移動します。この時、baseNode パラメータでコンテナ要素を指定してた場合は、fixed 要素もコンテナ要素の位置移動に追従し再配置されます。

baseX , baseY パラメータ

垂直または水平の単一方向にのみ baseNode パラメータ による基準位置設定を有効にすることができます。

上図のように、右辺についてはコンテナ要素を、底辺についてはブラウザの表示領域を基準に配置したい場合は、baseNode にコンテナ要素を指定し baseY パラメータを false とします。

$('#target').exFixed({
    baseNode : '#container',
    bottom : 0,
    right : 0,
    baseY : false
});

Demo

fixedX , fixedY パラメータ

垂直または水平の単一方向にのみ position:fixed を適用することができます。

上図のように、垂直 , 水平 方向にスクロール可能なページがあった時..

垂直方向にスクロールした場合、fixed 要素は他の要素に干渉せず固定位置に表示されたままとなります。

水平方向にスクロールした場合も同様に固定位置に表示されますが、要素の配置関係上、隣接する要素に重なって表示されてしまいます。

Demo

fixedX を false として水平方向への position:fixed を非適用状態にする事で、隣接要素への重なりを抑止できます。

$('#target').exFixed({
    top : 10,
    left : 10,
    fixedX : false
});

Demo

動作原理について

IE6 の場合は position:fixed を適用したくない方向の setExpression 記述を省略すればいいだけですが(関連記事)、その他のモダンブラウザについてはスクロール処理に合わせ fixed 要素の位置を調整する必要があります。単純な方法として、スクロールイベント内で position:fixed してる位置からスクロール量分を減算するという手がありますが、配置条件によっては対象要素がガタついて表示されてしまいます。

前回のエントリで、

只今、以前作成した exFixed.js という jQuery プラグインの機能拡張を行っており、その中で、

  • スクロールイベントの開始、終了時にのみ処理を行う
  • その際、スクロールイベントのスクロール方向に応じて処理を変える

という実装が必要になりました。

スクロールイベントの開始 / 終了 / 方向を判定する jQuery プラグインを書いてみた - Cyokodog::Diary

と言っていたのは、このガタつきを解消するのに必要だったためです。
具体的には、position:fixed させたくない方向にスクロールイベントが発生したら、以下処理を行う事でガタつきを抑止してます。

  • スクロールイベントの開始時に、position:fixed を absolute に切り替える
  • スクロールイベントの終了時に、position:absolute を fixed に切り替える
  • 上記の切替の際、その時点でのスクロール量、各種パラメータで指定された配置条件や単位(%など)を考慮した配置上の設定、復元を行う
resize メソッド

resize メソッドを使用すると動的な fixed 要素のサイズ変更 / 位置移動処理を簡単に記述することができます。
resize メソッドを使用せずに動的な fixed 要素のサイズ変更や位置移動をする場合、以下のように fixedOpen メソッドで変更可能状態にし、fixedClose メソッドで最終的なサイズや位置を確定する必要がありました。

fixedObj.fixedOpen(function(){
    fixedObj.fixedClose({
        top : 200,
        left : 200,
        width : 500,
        height : 300
    });
});

animate メソッド等を使用しない単純なサイズ変更や位置移動なら resize メソッドによる簡略化した記述が可能です。

fixedObj.resize({
    top : 200,
    left : 200,
    width : 500,
    height : 300
});

Demo

仕様変更について

exFixed オブジェクトの取得方法の変更

exFixed オブジェクトとは、前述の resize メソッドのサンプルでいうと "fixedObj" のことを指します。fixed 要素に対しリサイズ等の動的な変更を与える場合、exFixed オブジェクトの持つ各種メソッド(fixedOpen , fixedClose , fixedSize , resize )を使用する必要があります。
従来は以下のように exFixed メソッドの返却値を exFixed オブジェクトとする仕様になってました。

var fixedObj = $('#target').exFixed({
    bottom : 0,
    right : 0
});
fixedObj.fixedOpen( function(){....

メソッドチェーンという jQuery の基本思想の観点からすると、このようにプラグインメソッドが独自オブジェクトを返すことで、jQuery オブジェクトの連鎖を中断してしまうのは、あまりよろしくない設計かと思い直しまして、jQuery オブジェクトをそのまま返す仕様に変更しました。
プラグイン作成におけるこの辺の話は以下記事が詳しいです。

では exFixed オブジェクトはどのように取得すればいいかというと、メソッドチェーンで返された jQuery オブジェクトに getExFixed というメソッドがバインドされるので、これを実行し取得します。

Ver 1.3.0 より exAPI ベースの実装となりましたので、以下のように API オブジェクトを取得するようになります。

var api = $('div.fixed').exFixed({
    api : true,
    dynamicFixed : true,
    right : 100,
    bottom : 100
});

あとは従来通りの手法で fixed 要素を動的に扱うことができます。詳しくはこちらをご参照ください。

ちなみに今回新規に追加されたパラメータと併用して扱うことも可能です。(当然ですが)

Demo

exFixed オブジェクトの使用条件

exFixed メソッドはパラメータを省略して実行することができます。この場合は CSS での定義された位置やサイズが適用されます。

<style>
    #target{
        top : 10%;
        left : 20%;
        width : 50%;
        height :50%;
    }
</style>
<script>
    jQuery(function($){
        $('#target').exFixed(); // style で定義した値が反映される
    });
</script>

但し、exFixed オブジェクトを使用して fixed 要素に対し動的な操作を行う場合は、必ず exFixed メソッドにパラメータを指定する必要があります。パラメータには top(又は bottom)と left(又は right)の指定が必須となり、width や height も単位が % の場合は必須になります。

var fixedObj = $('#target').exFixed({
    top : '10%',
    left : '20%',
    width : '50%',
    height : '50%'
}).getExFixed();

var fixedObj.fixedOpen(function(){...

ちなみに、これらのパラメータを設定せず fixedOpen 等のメソッドを実行しても何も処理しない仕様になっています。

ご要望の対応

bosukete さんより以下ご要望を頂いておりました。

親要素の位置を基準とした、position:fixed 要素の配置を行いたい

希望されてる詳細な動作について確認したところ

  • ブラウザのリサイズにより親要素と fixed 要素の相対位置にずれが生じた場合、fixed 要素の位置を調整しずれを無くす
  • (水平スクロール時は指定要素に fixed 要素を追従させたいので)、垂直スクロール時にのみ position:fixed を適用する

という内容でした。今回の機能拡張により、以下記述で実現できるようになりました。

$('#target').exFixed({
    baseNode : '#container',
    top : 0,
    left : 0,
    fixedX : false
});

Demo

ダウンロード

こちらからどうぞ