[javascript] 메소드를 사용하여 jQuery 플러그인을 작성하는 방법은 무엇입니까?

호출하는 객체에 추가 기능 / 메소드를 제공하는 jQuery 플러그인을 작성하려고합니다. 온라인에서 읽은 모든 자습서 (지난 2 시간 동안 탐색 했음)에는 대부분 옵션을 추가하는 방법이 포함되지만 추가 기능은 포함되지 않습니다.

내가하고 싶은 일은 다음과 같습니다.

// 해당 div에 대한 플러그인을 호출하여 div를 메시지 컨테이너로 포맷

$("#mydiv").messagePlugin();
$("#mydiv").messagePlugin().saySomething("hello");

또는 그 라인을 따라 뭔가. 플러그인을 호출 한 다음 해당 플러그인과 관련된 함수를 호출합니다. 나는 이것을 할 수있는 방법을 찾지 못하는 것 같으며 많은 플러그인이 전에 그것을하는 것을 보았습니다.

플러그인에 대해 지금까지 가지고있는 내용은 다음과 같습니다.

jQuery.fn.messagePlugin = function() {
  return this.each(function(){
    alert(this);
  });

  //i tried to do this, but it does not seem to work
  jQuery.fn.messagePlugin.saySomething = function(message){
    $(this).html(message);
  }
};

어떻게 그런 걸 얻을 수 있습니까?

감사합니다!


2013 년 11 월 18 일 업데이트 : Hari의 다음 의견과 의견에 대한 정답을 변경했습니다.



답변

jQuery 플러그인 작성 페이지 ( http://docs.jquery.com/Plugins/Authoring ) 에 따르면 jQuery 및 jQuery.fn 네임 스페이스를 숨기지 않는 것이 가장 좋습니다. 그들은이 방법을 제안합니다 :

(function( $ ){

    var methods = {
        init : function(options) {

        },
        show : function( ) {    },// IS
        hide : function( ) {  },// GOOD
        update : function( content ) {  }// !!!
    };

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


})( jQuery );

기본적으로 함수를 배열 (랩핑 함수 범위)에 저장하고 전달 된 매개 변수가 문자열 인 경우 항목을 확인하고 매개 변수가 오브젝트 (또는 널) 인 경우 기본 메소드 ( “init”)로 되돌립니다.

그런 다음 메소드를 호출 할 수 있습니다.

$('div').tooltip(); // calls the init method
$('div').tooltip({  // calls the init method
  foo : 'bar'
});
$('div').tooltip('hide'); // calls the hide method
$('div').tooltip('update', 'This is the new tooltip content!'); // calls the update method

Javascripts “arguments”변수는 전달 된 모든 인수의 배열이므로 임의의 길이의 함수 매개 변수와 함께 작동합니다.


답변

추가 방법으로 플러그인을 만드는 데 사용한 패턴은 다음과 같습니다. 당신은 그것을 다음과 같이 사용할 것입니다 :

$('selector').myplugin( { key: 'value' } );

또는 메소드를 직접 호출하려면

$('selector').myplugin( 'mymethod1', 'argument' );

예:

;(function($) {

    $.fn.extend({
        myplugin: function(options,arg) {
            if (options && typeof(options) == 'object') {
                options = $.extend( {}, $.myplugin.defaults, options );
            }

            // this creates a plugin for each element in
            // the selector or runs the function once per
            // selector.  To have it do so for just the
            // first element (once), return false after
            // creating the plugin to stop the each iteration 
            this.each(function() {
                new $.myplugin(this, options, arg );
            });
            return;
        }
    });

    $.myplugin = function( elem, options, arg ) {

        if (options && typeof(options) == 'string') {
           if (options == 'mymethod1') {
               myplugin_method1( arg );
           }
           else if (options == 'mymethod2') {
               myplugin_method2( arg );
           }
           return;
        }

        ...normal plugin actions...

        function myplugin_method1(arg)
        {
            ...do method1 with this and arg
        }

        function myplugin_method2(arg)
        {
            ...do method2 with this and arg
        }

    };

    $.myplugin.defaults = {
       ...
    };

})(jQuery);


답변

이 방법은 어떻습니까?

jQuery.fn.messagePlugin = function(){
    var selectedObjects = this;
    return {
             saySomething : function(message){
                              $(selectedObjects).each(function(){
                                $(this).html(message);
                              });
                              return selectedObjects; // Preserve the jQuery chainability 
                            },
             anotherAction : function(){
                               //...
                               return selectedObjects;
                             }
           };
}
// Usage:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');

선택한 객체는 messagePlugin 클로저에 저장되며 해당 함수는 플러그인과 관련된 함수가 포함 된 객체를 반환합니다. 각 함수에서 현재 선택한 객체에 대해 원하는 작업을 수행 할 수 있습니다.

여기 에서 코드를 테스트하고 재생할 수 있습니다 .

편집 : jQuery 체인 기능을 유지하기 위해 코드가 업데이트되었습니다.


답변

현재 선택된 답변의 문제점은 실제로 생각하는 것처럼 선택기의 모든 요소에 대해 사용자 정의 플러그인의 새 인스턴스를 실제로 생성하지 않고 실제로 단일 인스턴스 만 생성하고 전달한다는 것입니다 선택기 자체가 범위입니다.

보기 이 바이올린을 더 깊은 설명.

대신 jQuery.each를 사용하여 선택기를 반복하고 선택기의 모든 요소에 대해 사용자 정의 플러그인의 새 인스턴스를 인스턴스화해야합니다.

방법은 다음과 같습니다.

(function($) {

    var CustomPlugin = function($el, options) {

        this._defaults = {
            randomizer: Math.random()
        };

        this._options = $.extend(true, {}, this._defaults, options);

        this.options = function(options) {
            return (options) ?
                $.extend(true, this._options, options) :
                this._options;
        };

        this.move = function() {
            $el.css('margin-left', this._options.randomizer * 100);
        };

    };

    $.fn.customPlugin = function(methodOrOptions) {

        var method = (typeof methodOrOptions === 'string') ? methodOrOptions : undefined;

        if (method) {
            var customPlugins = [];

            function getCustomPlugin() {
                var $el          = $(this);
                var customPlugin = $el.data('customPlugin');

                customPlugins.push(customPlugin);
            }

            this.each(getCustomPlugin);

            var args    = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;
            var results = [];

            function applyMethod(index) {
                var customPlugin = customPlugins[index];

                if (!customPlugin) {
                    console.warn('$.customPlugin not instantiated yet');
                    console.info(this);
                    results.push(undefined);
                    return;
                }

                if (typeof customPlugin[method] === 'function') {
                    var result = customPlugin[method].apply(customPlugin, args);
                    results.push(result);
                } else {
                    console.warn('Method \'' + method + '\' not defined in $.customPlugin');
                }
            }

            this.each(applyMethod);

            return (results.length > 1) ? results : results[0];
        } else {
            var options = (typeof methodOrOptions === 'object') ? methodOrOptions : undefined;

            function init() {
                var $el          = $(this);
                var customPlugin = new CustomPlugin($el, options);

                $el.data('customPlugin', customPlugin);
            }

            return this.each(init);
        }

    };

})(jQuery);

그리고 일하는 바이올린 .

첫 번째 바이올린에서 모든 div가 항상 정확히 같은 수의 픽셀로 오른쪽으로 이동하는 방법을 알 수 있습니다. 선택기의 모든 요소에 대해 하나의 옵션 오브젝트 만 존재하기 때문 입니다.

위에서 설명한 기술을 사용하면 두 번째 바이올린에서 각 div가 정렬되지 않고 무작위로 이동합니다 (랜덤 라이저의 첫 번째 div는 항상 89 행에서 1로 설정되므로 제외). 이제 선택기의 모든 요소에 대해 새 사용자 정의 플러그인 인스턴스를 올바르게 인스턴스화하고 있기 때문입니다. 모든 요소에는 자체 옵션 객체가 있으며 선택기에 저장되지 않고 사용자 정의 플러그인 자체에 저장됩니다.

즉, 새 jQuery 선택기에서 DOM의 특정 요소로 인스턴스화 된 사용자 정의 플러그인의 메소드에 액세스 할 수 있으며 첫 번째 바이올린에서와 같이 캐시하지 않아도됩니다.

예를 들어, 이것은 두 번째 바이올린의 기술을 사용하여 모든 옵션 객체의 배열을 반환합니다. 처음에는 undefined를 반환합니다.

$('div').customPlugin();
$('div').customPlugin('options'); // would return an array of all options objects

이것은 첫 번째 바이올린에서 옵션 객체에 액세스 해야하는 방법이며 배열이 아닌 단일 객체 만 반환합니다.

var divs = $('div').customPlugin();
divs.customPlugin('options'); // would return a single options object

$('div').customPlugin('options');
// would return undefined, since it's not a cached selector

현재 선택된 답변이 아닌 위의 기술을 사용하는 것이 좋습니다.


답변

jQuery는 Widget Factory 의 도입으로 이것을 훨씬 쉽게 만들었습니다 .

예:

$.widget( "myNamespace.myPlugin", {

    options: {
        // Default options
    },

    _create: function() {
        // Initialization logic here
    },

    // Create a public method.
    myPublicMethod: function( argument ) {
        // ...
    },

    // Create a private method.
    _myPrivateMethod: function( argument ) {
        // ...
    }

});

초기화 :

$('#my-element').myPlugin();
$('#my-element').myPlugin( {defaultValue:10} );

메소드 호출 :

$('#my-element').myPlugin('myPublicMethod', 20);

(이것은 jQuery UI 라이브러리가 구축되는 방식입니다.)


답변

더 간단한 방법은 중첩 함수를 사용하는 것입니다. 그런 다음 객체 지향 방식으로 연결할 수 있습니다. 예:

jQuery.fn.MyPlugin = function()
{
  var _this = this;
  var a = 1;

  jQuery.fn.MyPlugin.DoSomething = function()
  {
    var b = a;
    var c = 2;

    jQuery.fn.MyPlugin.DoSomething.DoEvenMore = function()
    {
      var d = a;
      var e = c;
      var f = 3;
      return _this;
    };

    return _this;
  };

  return this;
};

그것을 부르는 방법은 다음과 같습니다.

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();

그래도 조심하십시오. 중첩 함수는 생성 될 때까지 호출 할 수 없습니다. 그래서 당신은 이것을 할 수 없습니다 :

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
pluginContainer.MyPlugin.DoSomething();

DoEvenMore 함수를 작성하는 데 필요한 DoSomething 함수가 아직 실행되지 않았으므로 DoEvenMore 함수는 존재하지 않습니다. 대부분의 jQuery 플러그인의 경우 실제로 여기에 표시된 것과 같이 한 수준의 중첩 함수 만 있고 두 가지가 아닙니다.
중첩 함수를 생성 할 때 부모 함수의 다른 코드가 실행되기 전에 부모 함수의 시작 부분에서 이러한 함수를 정의해야합니다.

마지막으로 “this”멤버는 “_this”라는 변수에 저장됩니다. 중첩 함수의 경우 호출 클라이언트의 인스턴스에 대한 참조가 필요한 경우 “_this”를 반환해야합니다. jQuery 인스턴스가 아닌 함수에 대한 참조를 리턴하므로 중첩 함수에서 “this”만 리턴 할 수 없습니다. jQuery 참조를 리턴하면 리턴시 고유 jQuery 메소드를 연결할 수 있습니다.


답변

jQuery Plugin Boilerplate 에서 얻었습니다.

jQuery Plugin Boilerplate 에도 설명되어 있습니다.

// jQuery Plugin Boilerplate
// A boilerplate for jumpstarting jQuery plugins development
// version 1.1, May 14th, 2011
// by Stefan Gabos

// remember to change every instance of "pluginName" to the name of your plugin!
(function($) {

    // here we go!
    $.pluginName = function(element, options) {

    // plugin's default options
    // this is private property and is accessible only from inside the plugin
    var defaults = {

        foo: 'bar',

        // if your plugin is event-driven, you may provide callback capabilities
        // for its events. execute these functions before or after events of your
        // plugin, so that users may customize those particular events without
        // changing the plugin's code
        onFoo: function() {}

    }

    // to avoid confusions, use "plugin" to reference the
    // current instance of the object
    var plugin = this;

    // this will hold the merged default, and user-provided options
    // plugin's properties will be available through this object like:
    // plugin.settings.propertyName from inside the plugin or
    // element.data('pluginName').settings.propertyName from outside the plugin,
    // where "element" is the element the plugin is attached to;
    plugin.settings = {}

    var $element = $(element), // reference to the jQuery version of DOM element
    element = element; // reference to the actual DOM element

    // the "constructor" method that gets called when the object is created
    plugin.init = function() {

    // the plugin's final properties are the merged default and
    // user-provided options (if any)
    plugin.settings = $.extend({}, defaults, options);

    // code goes here

   }

   // public methods
   // these methods can be called like:
   // plugin.methodName(arg1, arg2, ... argn) from inside the plugin or
   // element.data('pluginName').publicMethod(arg1, arg2, ... argn) from outside
   // the plugin, where "element" is the element the plugin is attached to;

   // a public method. for demonstration purposes only - remove it!
   plugin.foo_public_method = function() {

   // code goes here

    }

     // private methods
     // these methods can be called only from inside the plugin like:
     // methodName(arg1, arg2, ... argn)

     // a private method. for demonstration purposes only - remove it!
     var foo_private_method = function() {

        // code goes here

     }

     // fire up the plugin!
     // call the "constructor" method
     plugin.init();

     }

     // add the plugin to the jQuery.fn object
     $.fn.pluginName = function(options) {

        // iterate through the DOM elements we are attaching the plugin to
        return this.each(function() {

          // if plugin has not already been attached to the element
          if (undefined == $(this).data('pluginName')) {

              // create a new instance of the plugin
              // pass the DOM element and the user-provided options as arguments
              var plugin = new $.pluginName(this, options);

              // in the jQuery version of the element
              // store a reference to the plugin object
              // you can later access the plugin and its methods and properties like
              // element.data('pluginName').publicMethod(arg1, arg2, ... argn) or
              // element.data('pluginName').settings.propertyName
              $(this).data('pluginName', plugin);

           }

        });

    }

})(jQuery);