Skip to content

Instantly share code, notes, and snippets.

@maepon
Created February 1, 2013 15:39
Show Gist options
  • Save maepon/4692053 to your computer and use it in GitHub Desktop.
Save maepon/4692053 to your computer and use it in GitHub Desktop.
jquery_plugin_authoring

Plugins/Authoring

プラグイン/作成

So you've become comfortable with jQuery and would like to learn how to write your own plugins. Great! You're in the right spot. Extending jQuery with plugins and methods is very powerful and can save you and your peers a lot of development time by abstracting your most clever functions into plugins. This post will outline the basics, best practices, and common pitfalls to watch out for as you begin writing your plugin.

jQueryを使うことが快適になってきたら、プラグインの作り方を知りたくなるでしょう。それは正解です!プラグインとメソッドでjQueryを利用することは、非常に協力で、さらに、プラグインの中に最も有効な機能を抽象化することで、開発にかける時間を大幅に節約出来ます。この記事は、プラグインを書き始める際の基本的な概要とベストプラクティス、さらに気をつける必要のある一般的な落とし穴についての記事です。

Contents

  1. Getting Started
  2. Context
  3. The Basics
  4. Maintaining Chainability
  5. Defaults and Options
  6. Namespacing
    6.1. Plugin Methods
    6.2. Events
    6.3. Data
  7. Summary and Best Practices
  8. Translations

目次

  1. さあはじめよう
  2. コンテキスト
  3. 基本
  4. メソッドチェーンの維持
  5. デフォルトとオプション
  6. 名前空間
    6.1. プラグインのメソッド
    6.2. イベント
    6.3. データ
  7. 概要とベストプラクティス
  8. 翻訳

Getting Started

さあはじめよう

To write a jQuery plugin, start by adding a new function property to the jQuery.fn object where the name of the property is the name of your plugin:

jQueryプラグインを書くのは、jQuery.fnオブジェクトに新しい関数のプロパティを追加することから開始します、その際のプロパティ名がプラグインの名前になります。

jQuery.fn.myPlugin = function() {

  // Do your awesome plugin stuff here

  // ここにあなたの素敵なプラグインを詰め込んでください

};

But wait! Where's my awesome dollar sign that I know and love? It's still there, however to make sure that your plugin doesn't collide with other libraries that might use the dollar sign, it's a best practice to pass jQuery to an IIFE (Immediately Invoked Function Expression) that maps it to the dollar sign so it can't be overwritten by another library in the scope of its execution.

ちょっと待って下さい!上記にはあのご存知の、あの愛している素晴らしいドルマークはどこにあるのでしょうか?そこに存在してます、とはいえ他のプラグインでもまず利用されているドルマークが衝突しないように確認する必要があります。そのためのjQueryでのベストプラクティスはIIFE(即時関数)で、そこに出てきたドルマークは、そのスコープ内で実行することで他のライブラリに上書きされることはなくなります。

(function( $ ) {
  $.fn.myPlugin = function() {

    // Do your awesome plugin stuff here

  // ここにあなたの素敵なプラグインを詰め込んでください

  };
})( jQuery );

Ah, that's better. Now within that closure, we can use the dollar sign in place of jQuery as much as we like.

これが良いでしょう。このクロージャー内である限り、jQueryオブジェクトが置き換えられたドルマークを好きなように使えます。

Context

コンテキスト

Now that we have our shell we can start writing our actual plugin code. But before we do that, I'd like to say a word about context. In the immediate scope of the plugin function, the this keyword refers to the jQuery object the plugin was invoked on. This is a common slip up due to the fact that in other instances where jQuery accepts a callback, the this keyword refers to the native DOM element. This often leads to developers unnecessarily wrapping the this keyword (again) in the jQuery function.

実際のプラグインのコードが記述できる領域を確保出来ました。しかし、その前にコンテキストについて解説します。プラグインの関数のスコープが有効な範囲内では、thisは呼び出されたプラグインのjQueryオブジェクトを参照しています。他のインスタンスではjQueryがコールバックを受け取った時ではthisはネイティブのDOM要素を参照しているので、ここは間違えやすいところです。これのせいでしばしば開発者は、不必要な'this'のjQuery関数でのラッピング(重複)を行なってしまいます。

(function( $ ){

  $.fn.myPlugin = function() {

    // there's no need to do $(this) because
    // "this" is already a jquery object

    // ここでは"this"はすでにjQueryオブジェクトなので 
    // $(this)を行う必要はありません

    // $(this) would be the same as $($('#element'));

    // ここでの$(this)は$($('#element'))と同じ事になります

    this.fadeIn('normal', function(){

      // the this keyword is a DOM element

      // ここでのthisはDOM要素になります

    });

  };
})( jQuery );
$('#element').myPlugin();

The Basics

基本

Now that we understand the context of jQuery plugins, let's write a plugin that actually does something.

jQueryプラグインのコンテキストを理解したので、実際に何か実行するプラグインを書いてみましょう。

(function( $ ){

  $.fn.maxHeight = function() {

    var max = 0;

    this.each(function() {
      max = Math.max( max, $(this).height() );
    });

    return max;
  };
})( jQuery );
var tallest = $('div').maxHeight(); // Returns the height of the tallest div

This is a simple plugin that leverages [.height()](/edit/Manipulation/height?redlink=1) to return the height of the tallest div in the page.

これは、.height()を利用して、ページ内で一番高いdivの高さを返すというシンプルなプラグインです。

Maintaining Chainability

メソッドチェーンの持続

The previous example returns an integer value of the tallest div on the page, but often times the intent of a plugin is simply modify the collection of elements in some way, and pass them along to the next method in the chain. This is the beauty of jQuery's design and is one of the reasons jQuery is so popular. So to maintain chainability in a plugin, you must make sure your plugin returns the this keyword.

上記の事例はページ内の一番高いdivの数値を返すものですが、プラグインはいくつかの方法で要素の集合をシンプルに書き換えていくというものが多く、続いている次のメソッドに対象の要素群を引き渡します。これはjQueryの言語デザインの美しさでもあり、jQueryがこれほど人気になった原因でもあります。ということでプラグインにおいてメソッドチェーンを持続させるために、プラグインはthisを返すようにする必要があります。

(function( $ ){

  $.fn.lockDimensions = function( type ) {

    return this.each(function() {

      var $this = $(this);

      if ( !type || type == 'width' ) {
        $this.width( $this.width() );
      }

      if ( !type || type == 'height' ) {
        $this.height( $this.height() );
      }

    });

  };
})( jQuery );
$('div').lockDimensions('width').css('color', 'red');

Because the plugin returns the this keyword in its immediate scope, it maintains chainability and the jQuery collection can continue to be manipulated by jQuery methods, such as .css. So if your plugin doesn't return an intrinsic value, you should always return the this keyword in the immediate scope of the plugin function. Also, as you might assume, arguments you pass in your plugin invocation get passed to the immediate scope of the plugin function. So in the previous example, the string 'width' becomes the type argument for the plugin function.

プラグインは直下のスコープ内のthisを返すことで、メソッドチェーンの持続ができ、jQueryは上記の例の.cssのようにjQueryのメソッドで操作を継続できます。もし、プラグインが固有の値を返すものでなければ、プラグインの関数直下のthisを返すようにしてください。また、あなたが考えているように、プラグインに渡された引数もプラグインの関数直下のスコープで呼び出します。上記の例では、'width'という文字列がプラグインの関数のtypeという引数になっています。

Defaults and Options

デフォルトとオプション

For more complex and customizable plugins that provide many options, it's a best practice to have default settings that can get extended (using [$.extend](/Utilities/jQuery.extend)) when the plugin is invoked. So instead of calling a plugin with a large number of arguments, you can call it with one argument which is an object literal of the settings you would like to override. Here's how you do it.

より複雑でカスタマイズができるプラグインは多くのオプションを用意しています。そのためのベストプラクティスはプラグインが呼び出された時に拡張可能($.extendを利用)なデフォルトのセッティングを持っておくことです。

(function( $ ){

  $.fn.tooltip = function( options ) {

    // Create some defaults, extending them with any options that were provided
    var settings = $.extend( {
      'location'         : 'top',
      'background-color' : 'blue'
    }, options);

    return this.each(function() {

      // Tooltip plugin code here

    });

  };
})( jQuery );
$('div').tooltip({
  'location' : 'left'
});

In this example, after calling the tooltip plugin with the given options, the default location setting gets overridden to become 'left', while the background-color setting remains the default 'blue'. So the final settings object ends up looking like this:

この例では、オプションを付加してtooltipプラグインを呼び出した後に、デフォルトの位置設定は'left'に上書きされる一方、背景色の設定はデフォルトの'blue'のままです。というわけで最終的な設定は以下のようになります。

{
  'location'         : 'left',
  'background-color' : 'blue'
}

This is a great way to offer a highly configurable plugin without requiring the developer to define all available options.

これは、利用可能なオプションを定義するために開発者を必要とすること無く、高度な設定を要求するすばらしい方法です。

Namespacing

名前空間の指定

Properly namespacing your plugin is a very important part of plugin development. Namespacing correctly assures that your plugin will have a very low chance of being overwritten by other plugins or code living on the same page. Namespacing also makes your life easier as a plugin developer because it helps you keep better track of your methods, events and data.

プラグインに適切な名前空間を指定することは、プラグイン開発において非常に重要です。名前空間を正しく指定することは、確実にプラグインが同一ページ内の他のプラグインや動作中のコードに上書きされる危険性を極めて小さくします。

Plugin Methods

プラグインのメソッド

Under no circumstance should a single plugin ever claim more than one namespace in the jQuery.fn object.

一つのプラグインがjQuery.fnオブジェクトに対して複数のネームスペースを要求しなければいけない状況というのは存在しません。

(function( $ ){

  $.fn.tooltip = function( options ) {
    // THIS

    // これは
  };
  $.fn.tooltipShow = function( ) {
    // IS

    // 悪い例
  };
  $.fn.tooltipHide = function( ) {
    // BAD

    // です
  };
  $.fn.tooltipUpdate = function( content ) {
    // !!!

    // !!!
  };

})( jQuery );

This is a discouraged because it clutters up the $.fn namespace. To remedy this, you should collect all of your plugin's methods in an object literal and call them by passing the string name of the method to the plugin.

これは$.fnのネームスペースを混乱させるので推奨しません。これに対処するには、オブジェクトリテラルにプラグインのメソッド全てを集約し、プラグインのメッソドの文字列を渡すことで呼び出す必要があります。

(function( $ ){

  var methods = {
    init : function( options ) {
      // THIS

      // これは
    },
    show : function( ) {
      // IS

      // 良い例
    },
    hide : function( ) {
      // GOOD

      // です
    },
    update : function( content ) {
      // !!!

      // !!!
    }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic

    // メソッド呼び出し部分
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }

  };

})( jQuery );

// calls the init method

// 初期化メソッドの呼び出し
$('div').tooltip();

// calls the init method

// 初期化メソッドの呼び出し
$('div').tooltip({
  foo : 'bar'
});
// calls the hide method

// hideメソッドの呼び出し
$('div').tooltip('hide');
// calls the update method

// updateメソッドの呼び出し
$('div').tooltip('update', 'This is the new tooltip content!');

This type of plugin architecture allows you to encapsulate all of your methods in the plugin's parent closure, and call them by first passing the string name of the method, and then passing any additional parameters you might need for that method. This type of method encapsulation and architecture is a standard in the jQuery plugin community and it used by countless plugins, including the plugins and widgets in jQueryUI.

このタイプのプラグインのアーキテクチャーは、全てのメソッドをプラグインの親クロージャーにカプセル化し、渡された最初の文字列によってメソッドが呼び出されます、さらに、そのメソッドが必要となる場合には追加のパラメーターも渡します。このカプセル化とアーキテクチャーは、jQueryプラグインのコミュニティではスタンダードであり、jQueryUIのプラグインやウィジェットも含めて数えきれないプラグインで活用されています。

Events

イベント

A lesser known feature of the bind method is that is allows for namespacing of bound events. If your plugin binds an event, its a good practice to namespace it. This way, if you need to unbind it later, you can do so without interfering with other events that might have been bound to the same type of event. You can namespace your events by appending “.” to the type of event you're binding.

あまり知られていませんがbindメソッドの機能は、バインドされたイベントの名前空間の利用ができるということです。もし、プラグインがイベントをバインドするのであれば、名前空間のよいプラクティスになります。この方法は、後ほどunbindする必要がある場合も、バインドされていた同様のタイプのイベントを干渉することはありません。イベントに対して、バインドしているイベントタイプに“.”を追加することで名前空間の指定ができます。

訳者注) bind/unbindメソッドは現在ではon/offメソッドと置き換えて読んだほうがいいでしょう。

(function( $ ){

  var methods = {
     init : function( options ) {

       return this.each(function(){
         $(window).bind('resize.tooltip', methods.reposition);
       });

     },
     destroy : function( ) {

       return this.each(function(){
         $(window).unbind('.tooltip');
       })

     },
     reposition : function( ) {
       // ...
     },
     show : function( ) {
       // ...
     },
     hide : function( ) {
       // ...
     },
     update : function( content ) {
       // ...
     }
  };

  $.fn.tooltip = function( method ) {

    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }

  };

})( jQuery );
$('#fun').tooltip();
// Some time later...

// 中略...
$('#fun').tooltip('destroy');

In this example, when the tooltip is initialized with the init method, it binds the reposition method to the resize event of the window under the namespace 'tooltip'. Later, if the developer needs to destroy the tooltip, we can unbind the events bound by the plugin by passing its namespace, in this case 'tooltip', to the unbind method. This allows us to safely unbind plugin events without accidentally unbinding events that may have been bound outside of the plugin.

この例では、tooltipがinitメソッドで初期化された際に、'tooltip'という名前空間で、ウィンドウのリサイズイベントに対してrepositionメソッドをバインドします。後からtooltipを破棄する必要が出てきた時には、アンバインドするメソッドの名前空間(今回のケースではtooltip)を渡すことで、プラグインがバインドしているイベントをアンバインドできます。

訳者注) bind/unbindメソッドは現在ではon/offメソッドと置き換えて読んだほうがいいでしょう。

Data

データ

Often times in plugin development, you may need to maintain state or check if your plugin has already been initialized on a given element. Using jQuery's data method is a great way to keep track of variables on a per element basis. However, rather than keeping track of a bunch of separate data calls with different names, it's best to use a single object literal to house all of your variables, and access that object by a single data namespace.

プラグインの開発中にしばしば、状態の維持もしくは与えられたメソッドが初期化済みであるかをチェックする必要がでてくることがあります。jQueryのdataメソッドを利用することは、要素ごとの値の補足を続けるには優れた方法です。しかしながら、異なる名前空間で分離したデータの束を補足し続けるよりも、ひとつの全ての値を保持する単一のオブジェクトリテラルの利用と、一つのデータの名前空間のオブジェクトにアクセスすることが一番です。

(function( $ ){

  var methods = {
     init : function( options ) {

       return this.each(function(){

         var $this = $(this),
             data = $this.data('tooltip'),
             tooltip = $('<div />', {
               text : $this.attr('title')
             });

         // If the plugin hasn't been initialized yet
         if ( ! data ) {

           /*
             Do more setup stuff here
           */

           $(this).data('tooltip', {
               target : $this,
               tooltip : tooltip
           });

         }
       });
     },
     destroy : function( ) {

       return this.each(function(){

         var $this = $(this),
             data = $this.data('tooltip');

         // Namespacing FTW
         $(window).unbind('.tooltip');
         data.tooltip.remove();
         $this.removeData('tooltip');

       })

     },
     reposition : function( ) { // ... },
     show : function( ) { // ... },
     hide : function( ) { // ... },
     update : function( content ) { // ...}
  };

  $.fn.tooltip = function( method ) {

    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }

  };

})( jQuery );

Using data helps you keep track of variables and state across method calls from your plugin. Namespacing your data into one object literal makes it easy to access all of your plugin's properties from one central location, as well as reducing the data namespace which allows for easy removal if need be.

dataを使うことで、プラグインからメソッドをまたいで変数やステータスの捕捉し続けるようになります。データを一つのオブジェクトリテラルで名前空間に指定することは、一箇所からプラグインのプロパティに容易にアクセスできるようになり、同様に必要があればすぐに削除して名前空間のデータを減少させることができます。

Summary and Best Practices

概要とベストプラクティス

Writing jQuery plugins allows you to make the most out of the library and abstract your most clever and useful functions out into reusable code that can save you time and make your development even more efficient. Here's a brief summary of the post and what to keep in mind when developing your next jQuery plugin:

jQueryのプラグインを書くことは、ライブラリを最大限に活用できるようになり、あなたの持つ最も合理的で有用な機能を時間を節約し開発をより合理的にしてくれる再利用可能なコードへと抽象化します。以下はこのエントリーの簡単な要約と次以降のjQueryプラグインを作成する際の注意点です。

  • Always wrap your plugin in a closure: (function( $ ){ /* plugin goes here */ })( jQuery );

  • 常にプラグインはクロージャーの中に(function( $ ){ /* プラグインはここに */ })( jQuery );

  • Don't redundantly wrap the this keyword in the immediate scope of your plugin's function

  • プラグインの関数直下のスコープ内のthisを重複してラップしない。

  • Unless you're returning an intrinsic value from your plugin, always have your plugin's function return the this keyword to maintain chainability.

  • プラグインが固有の値を返さない限りは、メソッドチェーンの維持のためにプラグインは常にthisを返す。

  • Rather than requiring a lengthy amount of arguments, pass your plugin settings in an object literal that can be extended over the plugin's defaults.

  • 長大な引数を要求するよりは、プラグインの設定をオブジェクトのリテラル内にそのプラグインのデフォルトとして拡張できるように用意する。

  • Don't clutter the jQuery.fn object with more than one namespace per plugin.

  • プラグインが複数のネームスペースを持つことでjQuery.fnオブジェクトをちらかさない。

  • Always namespace your methods, events and data.

  • メソッドとイベントとデータは常にネームスペースの中に。

Translations

翻訳

If you have translated this article or have some similar one on your blog post a link here. Please mark Full Translated articles with (t) and similar ones with (s).

この記事を翻訳したり、それに準じるようなブログの記事をポストした場合、ここにリンクしてください。完全な翻訳ならば(t)と準じるものなら(s)と印をしてください。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment