Как создать плагин jQuery с помощью методов?

Я пытаюсь написать плагин jQuery, который предоставит дополнительные функции/методы объекту, который его вызывает. Все обучающие материалы, которые я читал в Интернете (были просмотрены в течение последних 2 часов), включают, самое большее, как добавлять параметры, но не дополнительные функции.

Вот что я хочу сделать:

//format 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);
  }
};

Как я могу достичь чего-то подобного?

Спасибо!


Обновление 18 ноября 2013 года. Я изменил правильный ответ на вопрос Хари, следуя комментариям и рекомендациям.

Ответ 1

Согласно странице создания плагина 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" variable - это массив всех переданных аргументов, поэтому он работает с произвольными значениями параметров функции.

Ответ 2

Вот шаблон, который я использовал для создания плагинов с дополнительными методами. Вы бы использовали его как:

$('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);

Ответ 3

Как насчет этого подхода:

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.

Ответ 4

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.)

Ответ 5

Проблема с выбранным в данный момент ответом заключается в том, что вы фактически не создаете новый экземпляр настраиваемого плагина для каждого элемента селектора, который, как вы думаете, вы делаете... вы на самом деле создаете только один экземпляр и прохождение в селекторе в качестве области действия.

Просмотрите эту скрипту для более глубокого объяснения.

Вместо этого вам нужно будет пропустить селектор с помощью 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, поскольку он рандомизатор всегда установлен в 1 в строке 89). Это связано с тем, что теперь мы правильно создаем новый пользовательский экземпляр подключаемого модуля для каждого элемента селектора. Каждый элемент имеет свой собственный объект опций и не сохраняется в селекторе, но в экземпляре самого настраиваемого плагина.

Это означает, что вы сможете получить доступ к методам настраиваемого плагина, созданных на определенном элементе в DOM, из новых селекторов jQuery и не будут вынуждены кэшировать их, так как вы были бы в первом скрипте.

Например, это вернет массив всех объектов опций, используя технику во второй скрипке. Он вернет undefined в первый.

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

Вот как вам нужно будет получить доступ к объекту options в первом скрипте и будет возвращать только один объект, а не массив из них:

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

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

Я бы предложил использовать вышеприведенный метод, а не тот, который был выбран в текущем ответе.

Ответ 6

Более простой подход - использовать вложенные функции. Затем вы можете связать их объектно-ориентированным способом. Пример:

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", если вам нужна ссылка на экземпляр в вызывающем клиенте. Вы не можете просто вернуть "this" во вложенную функцию, потому что это вернет ссылку на функцию, а не на экземпляр jQuery. Возвращая ссылку на jQuery, вы можете возвращать встроенные методы jQuery.

Ответ 7

Я получил его из 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 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 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 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 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);

Ответ 8

Слишком поздно, но может быть, это может помочь кому-то в один прекрасный день.

У меня была такая же ситуация, как создание плагина jQuery с некоторыми методами, и после прочтения некоторых статей и некоторых шин я создаю шаблон плагина jQuery (https://github.com/acanimal/jQuery-Plugin-Boilerplate).

Кроме того, я разрабатываю с ним плагин для управления тегами (https://github.com/acanimal/tagger.js) и написал два сообщения в блоге, объясняющих шаг за шагом создание плагина jQuery (http://acuriousanimal.com/blog/2013/01/15/things-i-learned-creating-a-jquery-plugin-part-i/).

Ответ 9

Как насчет использования триггеров? Кто-нибудь знает какой-либо недостаток, используя их? Преимущество в том, что все внутренние переменные доступны через триггеры, а код очень прост.

Смотрите jsfiddle.

Пример использования

<div id="mydiv">This is the message container...</div>

<script>
    var mp = $("#mydiv").messagePlugin();

    // the plugin returns the element it is called on
    mp.trigger("messagePlugin.saySomething", "hello");

    // so defining the mp variable is not needed...
    $("#mydiv").trigger("messagePlugin.repeatLastMessage");
</script>

Plugin

jQuery.fn.messagePlugin = function() {

    return this.each(function() {

        var lastmessage,
            $this = $(this);

        $this.on('messagePlugin.saySomething', function(e, message) {
            lastmessage = message;
            saySomething(message);
        });

        $this.on('messagePlugin.repeatLastMessage', function(e) {
            repeatLastMessage();
        });

        function saySomething(message) {
            $this.html("<p>" + message + "</p>");
        }

        function repeatLastMessage() {
            $this.append('<p>Last message was: ' + lastmessage + '</p>');
        }

    });

}

Ответ 10

Здесь я хочу предложить шаги для создания простого плагина с аргументами.

JS

(function($) {
    $.fn.myFirstPlugin = function( options ) {

        // Default params
        var params = $.extend({
            text     : 'Default Title',
            fontsize : 10,
        }, options);
        return $(this).text(params.text);

    }
}(jQuery));

Здесь мы добавили объект по умолчанию params и задали значения по умолчанию для параметров с помощью функции extend. Следовательно, если мы передаем пустой аргумент, тогда он будет устанавливать значения по умолчанию, иначе он будет установлен.

HTML

$('.cls-title').myFirstPlugin({ text : 'Argument Title' });

Подробнее: Как создать плагин JQuery

Ответ 11

Вот моя версия с голыми костями. Как и ранее, вы бы назвали:

$('#myDiv').MessagePlugin({ yourSettings: 'here' })
           .MessagePlugin('saySomething','Hello World!');

- или напрямую обращаться к экземпляру @plugin_MessagePlugin

$elem = $('#myDiv').MessagePlugin();
var instance = $elem.data('plugin_MessagePlugin');
instance.saySomething('Hello World!');

MessagePlugin.js

;(function($){

    function MessagePlugin(element,settings){ // The Plugin
        this.$elem = element;
        this._settings = settings;
        this.settings = $.extend(this._default,settings);
    }

    MessagePlugin.prototype = { // The Plugin prototype
        _default: {
            message: 'Generic message'
        },
        initialize: function(){},
        saySomething: function(message){
            message = message || this._default.message;
            return this.$elem.html(message);
        }
    };

    $.fn.MessagePlugin = function(settings){ // The Plugin call

        var instance = this.data('plugin_MessagePlugin'); // Get instance

        if(instance===undefined){ // Do instantiate if undefined
            settings = settings || {};
            this.data('plugin_MessagePlugin',new MessagePlugin(this,settings));
            return this;
        }

        if($.isFunction(MessagePlugin.prototype[settings])){ // Call method if argument is name of method
            var args = Array.prototype.slice.call(arguments); // Get the arguments as Array
            args.shift(); // Remove first argument (name of method)
            return MessagePlugin.prototype[settings].apply(instance, args); // Call the method
        }

        // Do error handling

        return this;
    }

})(jQuery);

Ответ 12

Вы можете сделать:

(function ($) {

var YourPlugin = function (element, option) {
    var defaults = {
        //default value
    }

    this.option = $.extend({}, defaults, option);
    this.$element = $(element);
    this.init();
}

YourPlugin.prototype = {
    init: function () {
    },
    show: function() {

    },
    //another functions
}

$.fn.yourPlugin = function (option) {
    var arg = arguments,
        options = typeof option == 'object' && option;;
    return this.each(function () {
        var $this = $(this),
            data = $this.data('yourPlugin');

        if (!data) $this.data('yourPlugin', (data = new YourPlugin(this, options)));
        if (typeof option === 'string') {
            if (arg.length > 1) {
                data[option].apply(data, Array.prototype.slice.call(arg, 1));
            } else {
                data[option]();
            }
        }
    });
}; 
  });

Таким образом, ваш объект плагинов сохраняется как значение данных в вашем элементе.

 //Initialization without option
 $('#myId').yourPlugin();

 //Initialization with option
 $('#myId').yourPlugin({
        //your option
 });

//call show method
$('#myId').yourPlugin('show');

Ответ 13

Попробуйте следующее:

$.fn.extend({
"calendar":function(){
    console.log(this);
    var methods = {
            "add":function(){console.log("add"); return this;},
            "init":function(){console.log("init"); return this;},
            "sample":function(){console.log("sample"); return this;}
    };

    methods.init(); // you can call any method inside
    return methods;
}}); 
$.fn.calendar() // caller or 
$.fn.calendar().sample().add().sample() ......; // call methods

Ответ 14

Фактически это можно сделать "хорошим" способом, используя defineProperty. Где "хороший" означает без использования (), чтобы получить пространство имен плагинов или не передавать имя функции по строке.

Совместимость nit: defineProperty не работает в старых браузерах, таких как IE8 и ниже. Предостережение $.fn.color.blue.apply(foo, args) не будет работать, вам нужно использовать foo.color.blue.apply(foo, args).

function $_color(color)
{
    return this.css('color', color);
}

function $_color_blue()
{
    return this.css('color', 'blue');
}

Object.defineProperty($.fn, 'color',
{
    enumerable: true,
    get: function()
    {
        var self = this;

        var ret = function() { return $_color.apply(self, arguments); }
        ret.blue = function() { return $_color_blue.apply(self, arguments); }

        return ret;
    }
});

$('#foo').color('#f00');
$('#bar').color.blue();

ссылка JSFiddle

Ответ 15

В соответствии со стандартом jquery вы можете создать плагин следующим образом:

(function($) {

    //methods starts here....
    var methods = {
        init : function(method,options) {
             this.loadKeywords.settings = $.extend({}, this.loadKeywords.defaults, options);
             methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
             $loadkeywordbase=$(this);
        },
        show : function() {
            //your code here.................
        },
        getData : function() {
           //your code here.................
        }

    } // do not put semi colon here otherwise it will not work in ie7
    //end of methods

    //main plugin function starts here...
    $.fn.loadKeywords = function(options,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 ecw-Keywords');
        }
    };
    $.fn.loadKeywords.defaults = {
            keyName:     'Messages',
            Options:     '1',
            callback: '',
    };
    $.fn.loadKeywords.settings = {};
    //end of plugin keyword function.

})(jQuery);

Как вызвать этот плагин?

1.$('your element').loadKeywords('show',{'callback':callbackdata,'keyName':'myKey'}); // show() will be called

Ссылка: ссылка

Ответ 16

Я думаю, это может помочь вам...

(function ( $ ) {
  
    $.fn.highlight = function( options ) {
  
        // This is the easiest way to have default options.
        var settings = $.extend({
            // These are the defaults.
            color: "#000",
            backgroundColor: "yellow"
        }, options );
  
        // Highlight the collection based on the settings variable.
        return this.css({
            color: settings.color,
            backgroundColor: settings.backgroundColor
        });
  
    };
  
}( jQuery ));

Ответ 17

Ниже приведен небольшой плагин для использования метода предупреждения для отладки. Сохраните этот код в файле jquery.debug.js: JS:

jQuery.fn.warning = function() {
   return this.each(function() {
      alert('Tag Name:"' + $(this).prop("tagName") + '".');
   });
};

HTML:

<html>
   <head>
      <title>The jQuery Example</title>

      <script type = "text/javascript" 
         src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

      <script src = "jquery.debug.js" type = "text/javascript"></script>

      <script type = "text/javascript" language = "javascript">
         $(document).ready(function() {
            $("div").warning();
            $("p").warning();
         });
      </script> 
   </head>

   <body>
      <p>This is paragraph</p>
      <div>This is division</div>
   </body>

</html>

Ответ 18

Вот как я это делаю:

(function ( $ ) {

$.fn.gridview = function( options ) {

    ..........
    ..........


    var factory = new htmlFactory();
    factory.header(...);

    ........

};

}( jQuery ));


var htmlFactory = function(){

    //header
     this.header = function(object){
       console.log(object);
  }
 }

Ответ 19

Что вы сделали, это в основном расширение объекта jQuery.fn.messagePlugin с помощью нового метода. Это полезно, но не в вашем случае.

Вам нужно использовать эту технику

function methodA(args){ this // refers to object... }
function saySomething(message){ this.html(message);  to first function }

jQuery.fn.messagePlugin = function(opts) {
  if(opts=='methodA') methodA.call(this);
  if(opts=='saySomething') saySomething.call(this, arguments[0]); // arguments is an array of passed parameters
  return this.each(function(){
    alert(this);
  });

};

Но вы можете выполнить то, что хотите, я имею в виду, что есть способ сделать $( "# mydiv" ). messagePlugin(). saySomething ( "hello" ); Мой друг, он начал писать о lugins и о том, как продлить их с помощью вашей цепочки функций, вот ссылка на его блог