jQuery UI ウィジェット・ファクトリー(Widget Factory)の使い方

プログレスバーを作成します。以下の例のように、これは次のものを呼び出すことでjQuery.widget()完了します。これには2つの引数があります。1つは作成するプラグインの名前、もう1つはプラグインをサポートする関数を含むオブジェクトリテラルです。プラグインが呼び出されると、新しいプラグインインスタンスが作成され、すべての関数はそのインスタンスのコンテキストで実行されます。これは標準のjQueryプラグインと2つの重要な点で異なります。まず、コンテキストはオブジェクトであり、DOM要素ではありません。次に、コンテキストは常に単一のオブジェクトであり、コレクションではありません。

$.widget( "custom.progressbar", {
    _create: function() {
        var progress = this.options.value + "%";
        this.element
            .addClass( "progressbar" )
            .text( progress );
    }
});

プラグイン名には名前空間が必須です。この例では、custom名前空間を使用しています。名前空間は1階層の深さまでしか作成できません。したがって、custom.progressbarは有効なプラグイン名ですが、very.custom.progressbarは有効なプラグイン名ではありません。

ウィジェット・ファクトリー(Widget Factory)が2つのプロパティを提供することがわかります。this.elementは、1つの要素を含むjQueryオブジェクトです。複数の要素を含むjQueryオブジェクトに対してプラグインを呼び出すと、要素ごとに個別のプラグインインスタンスが作成され、各インスタンスは独自のthis.elementを持ちます。2つ目のプロパティは、this.options、すべてのプラグインオプションを含むキー名と値のペアのハッシュです。これらのオプションは、次のようにプラグインに渡すことができます。

$( "<div></div>" )
    .appendTo( "body" )
    .progressbar({ value: 20 });

私たちが次のものを呼び出すと、jQuery.widget()、それは次のものにjQuery.fn(標準プラグインを作成するためのシステム)に関数を追加してjQueryを拡張します。追加される関数名は、次のものに渡すjQuery.widget()名前(名前空間なし)に基づいています - "progressbar"。プラグインに渡されるオプションは、プラグインインスタンスで設定された値を取得します。以下の例のように、任意のオプションにデフォルト値を指定できます。APIを設計する際は、プラグインの最も一般的な使用ケースを明確にして、適切なデフォルト値を設定できるようにし、すべてのオプションを本当にオプションにしてください。

$.widget( "custom.progressbar", {
 
    // Default options.
    options: {
        value: 0
    },
    _create: function() {
        var progress = this.options.value + "%";
        this.element
            .addClass( "progressbar" )
            .text( progress );
    }
});

プラグインメソッドの呼び出し

プログレスバーを初期化できるようになりました。プラグインインスタンスに対してメソッドを呼び出してアクションを実行します。プラグインメソッドを定義するには、私たちが次のものに渡すjQuery.widget()オブジェクト内で関数を参照するだけです。また、関数名の前にアンダースコアを付けることで「private」メソッドを定義することもできます。

$.widget( "custom.progressbar", {
 
    options: {
        value: 0
    },
 
    _create: function() {
        var progress = this.options.value + "%";
        this.element
            .addClass( "progressbar" )
            .text( progress );
    },
 
    // Create a public method.
    value: function( value ) {
 
        // No value passed, act as a getter.
        if ( value === undefined ) {
            return this.options.value;
        }
 
        // Value passed, act as a setter.
        this.options.value = this._constrain( value );
        var progress = this.options.value + "%";
        this.element.text( progress );
    },
 
    // Create a private method.
    _constrain: function( value ) {
        if ( value > 100 ) {
            value = 100;
        }
        if ( value < 0 ) {
            value = 0;
        }
        return value;
    }
});

プラグインインスタンスでメソッドを呼び出すには、jQueryプラグインにメソッド名を渡します。呼び出すメソッドが引数を受け取る場合は、メソッド名の後ろに引数を渡すだけです。

注意:プラグインの初期化に使用した同じjQuery関数にメソッド名を渡すことでメソッドを実行します。これにより、チェーンメソッド呼び出しを維持しながら、jQueryの名前空間の汚染を防ぎます。この章の後半では、より自然に見える他の使い方を見ていきます。

var bar = $( "<div></div>" )
    .appendTo( "body" )
    .progressbar({ value: 20 });
 
// Get the current value.
alert( bar.progressbar( "value" ) );
 
// Update the value.
bar.progressbar( "value", 50 );
 
// Get the current value again.
alert( bar.progressbar( "value" ) );

オプションの使用

option()メソッドはプラグインに自動的に提供されます。option()メソッドを使用すると、初期化後にオプションを取得および設定できます。このメソッドはjQueryの.css()と.attr()メソッドと同様です。名前だけを渡してゲッターとして使用したり、名前と値を渡してセッターとして使用したり、キー名と値のペアのハッシュを渡して複数の値を設定したりできます。ゲッターとして使用すると、プラグインは渡された名前に対応するオプションの現在値を返します。セッターとして使用すると、プラグインの_setOptionメソッドは、設定される各オプションに対して呼び出されます。プラグイン内で_setOptionメソッドを指定して、オプションの変更に対応できます。オプションの変更とは独立して実行するアクションについては、オーバーライドできます。_setOptions。

$.widget( "custom.progressbar", {
    options: {
        value: 0
    },
    _create: function() {
        this.options.value = this._constrain(this.options.value);
        this.element.addClass( "progressbar" );
        this.refresh();
    },
    _setOption: function( key, value ) {
        if ( key === "value" ) {
            value = this._constrain( value );
        }
        this._super( key, value );
    },
    _setOptions: function( options ) {
        this._super( options );
        this.refresh();
    },
    refresh: function() {
        var progress = this.options.value + "%";
        this.element.text( progress );
    },
    _constrain: function( value ) {
        if ( value > 100 ) {
            value = 100;
        }
        if ( value < 0 ) {
            value = 0;
        }
        return value;
    }
});

コールバックの追加

プラグインを拡張する最も簡単な方法はコールバックを追加することです。これにより、ユーザーはプラグインの状態が変化したときに反応できます。次の例では、進捗が100%に達したときにプログレスバーにコールバックを追加する方法を示しています。_trigger()メソッドには3つの引数があります:コールバック名、コールバックをトリガーするjQueryイベントオブジェクト、イベントに関連するデータハッシュです。コールバック名は唯一の必須引数ですが、プラグインにカスタム機能を実装したいユーザーにとって、他の引数は非常に役立ちます。例えば、ドラッグ可能なプラグインを作成する場合、ドラッグコールバックをトリガーするときにmousemoveイベントを渡すことができます。これにより、ユーザーはイベントオブジェクトが提供するx/y座標に基づいてドラッグに反応できます。注意してください、次のものに渡される_trigger()元のイベントは、jQueryイベントである必要があり、ネイティブブラウザイベントではありません。

$.widget( "custom.progressbar", {
    options: {
        value: 0
    },
    _create: function() {
        this.options.value = this._constrain(this.options.value);
        this.element.addClass( "progressbar" );
        this.refresh();
    },
    _setOption: function( key, value ) {
        if ( key === "value" ) {
            value = this._constrain( value );
        }
        this._super( key, value );
    },
    _setOptions: function( options ) {
        this._super( options );
        this.refresh();
    },
    refresh: function() {
        var progress = this.options.value + "%";
        this.element.text( progress );
        if ( this.options.value == 100 ) {
            this._trigger( "complete", null, { value: 100 } );
        }
    },
    _constrain: function( value ) {
        if ( value > 100 ) {
            value = 100;
        }
        if ( value < 0 ) {
            value = 0;
        }
        return value;
    }
});

コールバック関数は本質的に追加のオプションにすぎないため、他のオプションと同じように取得および設定できます。コールバックが実行されるたびに、対応するイベントがトリガーされます。イベントタイプは、プラグイン名とコールバック関数名を連結して決定されます。コールバックとイベントはどちらも、イベントオブジェクトとイベントに関連するデータハッシュの2つの同じ引数を受け取ります。具体的には次の例のとおりです。

プラグインには、ユーザーがアクションを実行できないようにする機能が必要かもしれません。これを行う最善の方法は、キャンセル可能なコールバックを作成することです。ユーザーは、ネイティブイベントをキャンセルするのと同様に、コールバックまたは関連イベントをキャンセルできます。これは、次のものを呼び出すか、event.preventDefault()または返すfalseことによって実現されます。ユーザーがコールバックをキャンセルした場合、_trigger()メソッドはfalse、これによりプラグイン内で適切な機能を実装できます。

var bar = $( "<div></div>" )
    .appendTo( "body" )
    .progressbar({
        complete: function( event, data ) {
            alert( "Callbacks are great!" );
        }
    })
    .bind( "progressbarcomplete", function( event, data ) {
        alert( "Events bubble and support many handlers for extreme flexibility." );
        alert( "The progress bar value is " + data.value );
    });
 
bar.progressbar( "option", "value", 100 );

本質

ウィジェット・ファクトリー(Widget Factory)を使用してプラグインを作成する方法を見てきました。次に、それが実際にどのように機能するかを見てみましょう。次のものを呼び出すと、jQuery.widget()、プラグインのコンストラクタが作成され、プラグインインスタンスに渡すオブジェクトがプロトタイプとして設定されます。プラグインに自動的に追加されるすべての機能は、基本ウィジェットプロトタイプに由来します。そのプロトタイプは次のように定義されます。jQuery.Widget.prototypeプラグインインスタンスを作成するときは、次のものを使いますjQuery.dataそれによって、元のDOM要素にプラグイン名をキーとして格納します。

プラグインインスタンスはDOM要素に直接リンクされているため、プラグインメソッドを介さずにプラグインインスタンスに直接アクセスできます。これにより、メソッド名を文字列で渡すことなく、プラグインインスタンスで直接メソッドを呼び出せます。また、プラグインのプロパティにも直接アクセスできます。

var bar = $( "<div></div>" )
    .appendTo( "body" )
    .progressbar()
    .data( "progressbar" );
 
// Call a method directly on the plugin instance.
bar.option( "value", 50 );
 
// Access properties on the plugin instance.
alert( bar.options.value );

プラグインメソッドを介さずにインスタンスを作成することもできます。オプションと要素を指定してコンストラクタを直接呼び出すだけです:

var bar = $.custom.progressbar( {}, $( "<div></div>" ).appendTo( "body") );
 
// Same result as before.
alert( bar.options.value );

プラグインのプロトタイプを拡張する

プラグインがコンストラクタとプロトタイプを持つ最大の利点は、プラグインを簡単に拡張できることです。プラグインのプロトタイプにメソッドを追加または変更することで、プラグインのすべてのインスタンスの動作を変更できます。例えば、プログレスバーに進捗を0%にリセットするメソッドを追加したい場合、プロトタイプにこのメソッドを追加すると、すべてのプラグインインスタンスで呼び出し可能になります。

$.custom.progressbar.prototype.reset = function() {
    this._setOption( "value", 0 );
};

ウィジェットの拡張方法、および既存のウィジェット上に新しいウィジェットを作成する方法の詳細については、次のものを参照してください。ウィジェット・ファクトリー(Widget Factory)によるウィジェット(Widget)の拡張。

クリーンアップ

場合によっては、ユーザーがプラグインを適用し、その後で適用を解除できるようにする必要があります。次のものを使って_destroy()メソッドでこれを実現できます。_destroy()メソッド内では、初期化中およびその後の使用中にプラグインが行ったすべてのアクションを元に戻す必要があります。_destroy()は、次のものによって.destroy()メソッドが呼び出されます。.destroy()メソッドは、プラグインインスタンスがバインドされている要素がDOMから削除されるときに自動的に呼び出されるため、ガベージコレクションに使用できます。基本の.destroy()メソッドは、ウィジェットのDOM要素からインスタンス参照を削除する、要素からウィジェットの名前空間内のすべてのイベントのバインドを解除する、次のものを使って_bind()追加されたイベントのバインドを解除するなど、一般的なクリーンアップ操作も処理します。

$.widget( "custom.progressbar", {
    options: {
        value: 0
    },
    _create: function() {
        this.options.value = this._constrain(this.options.value);
        this.element.addClass( "progressbar" );
        this.refresh();
    },
    _setOption: function( key, value ) {
        if ( key === "value" ) {
            value = this._constrain( value );
        }
        this._super( key, value );
    },
    _setOptions: function( options ) {
        this._super( options );
        this.refresh();
    },
    refresh: function() {
        var progress = this.options.value + "%";
        this.element.text( progress );
        if ( this.options.value == 100 ) {
            this._trigger( "complete", null, { value: 100 } );
        }
    },
    _constrain: function( value ) {
        if ( value > 100 ) {
            value = 100;
        }
        if ( value < 0 ) {
            value = 0;
        }
        return value;
    },
    _destroy: function() {
        this.element
            .removeClass( "progressbar" )
            .text( "" );
    }
});

クロージング・ノート

ウィジェット・ファクトリー(Widget Factory)は、ステートフルなプラグインを作成するための1つの方法にすぎません。ここでは、他にもさまざまなモデルを使用でき、それぞれに長所と短所があります。ウィジェット・ファクトリー(Widget Factory)は多くの一般的な問題を解決し、効率を大幅に向上させるとともに、コードの再利用性も大幅に高めるため、jQuery UIやその他のステートフルなプラグインに適しています。

この章では、custom名前空間を使用したことに注意してください。ui名前空間は、公式のjQuery UIプラグイン用に予約されています。独自のプラグインを作成する場合は、独自の名前空間を作成する必要があります。これにより、プラグインがどこから来て、どのスコープに属しているかがより明確になります。

その他の拡張