如何使用方法创建jQuery插件?

Yuv*_*rmi 188 javascript jquery jquery-plugins javascript-framework

我正在尝试编写一个jQuery插件,它将为调用它的对象提供附加的函数/方法.我在线阅读的所有教程(过去2小时内一直在浏览)包括最多如何添加选项,但不包括其他功能.

这是我要做的事情:

//通过调用该div的插件将div格式化为消息容器

$("#mydiv").messagePlugin();
$("#mydiv").messagePlugin().saySomething("hello");
Run Code Online (Sandbox Code Playgroud)

或类似的规定.以下是它归结为:我调用插件,然后调用与该插件相关联的函数.我似乎找不到办法做到这一点,我见过许多插件之前做过.

这是我到目前为止插件的内容:

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);
  }
};
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现这样的目标?

谢谢!


2013年11月18日更新:我已经改变了Hari的评论和赞成的正确答案.

Har*_*ngh 307

根据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 );
Run Code Online (Sandbox Code Playgroud)

基本上,您将函数存储在数组中(作用于包装函数),如果传递的参数是字符串,则检查条目,如果参数是对象(或null),则恢复为默认方法(此处为"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
Run Code Online (Sandbox Code Playgroud)

Javascripts"arguments"变量是传递的所有参数的数组,因此它适用于任意长度的函数参数.

  • 对于那些第一次说"变量来自哪里"的人来说 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments - 我一直在使用JS从来不知道.你每天学习新的东西! (15认同)
  • 这种技术存在一个主要问题!它不像你认为的那样为选择器中的每个元素创建一个新实例,而是只创建一个附加到选择器本身的单个实例.查看[我的回答](http://stackoverflow.com/a/22976877/1790054)获取解决方案. (4认同)
  • 这是我使用的方法.你也可以通过$ .fn.tooltip('methodname',params)静态调用这些方法; (2认同)
  • @DiH,我和你在一起.这种方法似乎很棒,但它不允许您从`init`以外的任何地方访问您的全局设置. (2认同)

tva*_*son 56

这是我用于使用其他方法创建插件的模式.您会像以下一样使用它:

$('selector').myplugin( { key: 'value' } );
Run Code Online (Sandbox Code Playgroud)

或者,直接调用方法,

$('selector').myplugin( 'mymethod1', 'argument' );
Run Code Online (Sandbox Code Playgroud)

例:

;(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);
Run Code Online (Sandbox Code Playgroud)

  • 与jquery-ui相同的模式,我不喜欢所有的魔术字符串,但还有其他任何方式! (9认同)
  • 这似乎是一种非标准的做事方式 - 有没有比这更简单的东西,比如链接功能?谢谢! (7认同)
  • @GusDeCooL它只是确保我们正在启动一个新语句,这样我们的函数定义就不会被解释为其他人格式不正确的Javascript的参数(即,初始paren不被视为函数调用操作符).见http://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function (4认同)
  • @yuval - 通常jQuery插件返回jQuery或值,而不是插件本身.这就是为什么当你想调用插件时,方法的名称作为参数传递给插件.您可以传递任意数量的参数,但是您必须调整函数和参数解析.可能最好将它们设置在您展示的匿名对象中. (2认同)

CMS*_*CMS 35

这种方法怎么样:

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');
Run Code Online (Sandbox Code Playgroud)

所选对象存储在messagePlugin闭包中,该函数返回一个包含与插件关联的函数的对象,在每个函数中,您可以对当前选定的对象执行所需的操作.

您可以在此处测试和播放代码.

编辑:更新代码以保持jQuery可链接性的强大功能.

  • 这种方法的主要问题是它不能保留`$('p').messagePlugin()`之后的可链接性,除非你调用它返回的两个函数之一. (4认同)
  • 每次调用messagePlugin()时,它都会创建一个具有这两个函数的新对象,不是吗? (3认同)

Kev*_*ski 17

当前选择的答案的问题在于,您实际上并没有为选择器中的每个元素创建自定义插件的新实例,就像您认为的那样......实际上您只是创建单个实例并传入选择器本身作为范围.

查看这个小提琴以获得更深入的解释.

相反,您需要使用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);
Run Code Online (Sandbox Code Playgroud)

还有一个工作小提琴.

您会注意到,在第一个小提琴中,所有div总是向右移动完全相同的像素数.这是因为选择器中的所有元素只存在一个选项对象.

使用上面描述的技术,您会注意到,在第二个小提琴中,每个div都没有对齐并随机移动(不包括第一个div,因为它的随机函数总是在第89行设置为1).那是因为我们现在正在为选择器中的每个元素正确实例化一个新的自定义插件实例.每个元素都有自己的选项对象,不会保存在选择器中,而是保存在自定义插件本身的实例中.

这意味着您将能够从新的jQuery选择器访问在DOM中的特定元素上实例化的自定义插件的方法,并且不会像在第一个小提琴中那样强制缓存它们.

例如,这将使用第二个小提琴中的技术返回所有选项对象的数组.它将在第一个中返回undefined.

$('div').customPlugin();
$('div').customPlugin('options'); // would return an array of all options objects
Run Code Online (Sandbox Code Playgroud)

这是你必须在第一个小提琴中访问options对象的方法,并且只返回一个对象,而不是它们的数组:

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
Run Code Online (Sandbox Code Playgroud)

我建议使用上面的技术,而不是当前选择的答案.


Yar*_*rin 15

通过引入Widget Factory, jQuery使这变得更容易.

例:

$.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 ) {
        // ...
    }

});
Run Code Online (Sandbox Code Playgroud)

初始化:

$('#my-element').myPlugin();
$('#my-element').myPlugin( {defaultValue:10} );
Run Code Online (Sandbox Code Playgroud)

方法调用:

$('#my-element').myPlugin('myPublicMethod', 20);
Run Code Online (Sandbox Code Playgroud)

(这就是jQuery UI库的构建方式.)


小智 13

更简单的方法是使用嵌套函数.然后,您可以以面向对象的方式链接它们.例:

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;
};
Run Code Online (Sandbox Code Playgroud)

以下是如何调用它:

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
Run Code Online (Sandbox Code Playgroud)

但要小心.在创建嵌套函数之前,不能调用它.所以你不能这样做:

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
pluginContainer.MyPlugin.DoSomething();
Run Code Online (Sandbox Code Playgroud)

DoEvenMore函数甚至不存在,因为尚未运行DoSomething函数,这是创建DoEvenMore函数所必需的.对于大多数jQuery插件,你真的只会有一个级别的嵌套函数而不是我在这里展示的两个.
只需确保在创建嵌套函数时,在父函数的任何其他代码执行之前,在父函数的开头定义这些函数.

最后,请注意"this"成员存储在名为"_this"的变量中.对于嵌套函数,如果需要对调用客户端中的实例的引用,则应返回"_this".您不能只在嵌套函数中返回"this",因为它将返回对函数的引用而不是jQuery实例.返回jQuery引用允许您在返回时链接内部jQuery方法.

  • 这不起作用.如果你在两个不同的容器上调用插件,内部变量会被覆盖(即_this) (6认同)
  • 太好了-我只想知道为什么jQuery似乎更喜欢按.plugin('method')模式中的名称来调用方法? (2认同)
  • 一般来说函数里面的函数不是个好主意...... (2认同)

Sal*_*lim 9

我是从jQuery Plugin Boilerplate获得的

jQuery插件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);
Run Code Online (Sandbox Code Playgroud)


Eri*_*ron 6

太晚了,也许有一天它可以帮助别人.

我在同样的情况下,用一些方法创建一个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 /).


Maz*_*ari 5

你可以做:

(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]();
        }
      }
    });
  };
});
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您的 plugins 对象将作为数据值存储在您的元素中。

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

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

// call show method
$('#myId').yourPlugin('show');
Run Code Online (Sandbox Code Playgroud)