有没有更好的方法/模式来编写这个jQuery插件?

时间:2012-03-01 23:44:17

标签: javascript jquery design-patterns jquery-plugins

我有一个相当复杂的搜索插件:它具有不同版本的UI和功能,以及相互依赖的domElements中的一堆。插件的多个实例将同时存在于页面上。

我使用的是基本的jQuery创作模式:http://docs.jquery.com/Plugins/Authoring

为了保存选项,相互依赖的事件以及跨多个对象的各种dom查找,我将有问题的元素传递给每个函数,并将状态/选项/相互依赖性存储在数据属性中每次检索。它可以工作,并防止事件发生冲突,但编写代码似乎是一种混乱的方式。

跨多个实例存储状态的最佳方法是什么?我这样做的方式是一个巨大的矫枉过正,我错过了什么?它可能源于我在jQuery插件模式中创建类对象的误解。

(function($) {
var _options = {};

var methods = {
    init: function(options) {
        return this.each(function() {
            if (options) {
                _options = $.extend($.fn.examplePlugin.defaults, options);
            } else {
                _options = $.fn.examplePlugin.defaults;
            }
            $this = $(this);
            var data = $this.data('examplePlugin');
            if (!data) {

                $this.data('examplePlugin', {
                    target: $this
                });
                $.each(_options, function(key, value){
                    $this.data('examplePlugin')[key] = value;
                });
                data = $this.data('examplePlugin');
            }
            //Cache dom fragment plugin is in (if passed)
            if (data.domContextSelector == null || data.domContextSelector == "") {
                data.domContext = $(body);
            } else {
                data.domContext = $(data.domContextSelector);
            }
            init($this);
        });
    }
};
var init = function(element) {
    data = getData(element);
    //Storing dom elements to avoid lookups
    data.relatedElement = $(data.relatedElementSelector, data.domContext);
    element.click(function(event){
        doSomethingCool($(event.currentTarget));
    });
};
var doSomethingCool = function(element) {
    data = getData(element);
    element.slideUp();
    data.relatedElement.slideDown();
};
var adjustHeight = function(element) {
    data = getData(element);
    element.height(data.relatedElement.height());
};
var getData = function(element) {
    return $(element).data('examplePlugin');
};

$.fn.examplePlugin = 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.examplePlugin');
    }
    return false;
};
$.fn.examplePlugin.defaults = {
    defaultA: 'something',
    relatedElementSelector: '#related',
    domContextSelector: 'header.header'
};})(jQuery);

3 个答案:

答案 0 :(得分:2)

是的,如果您遵循jQuery指南,那么您将根据它应该如何构建并利用它的设计(特别是链接)来构建它。

但是,我不一定遵循这条道路。有很多方法可以做这些插件,take for example this guy为jQuery插件做了一个样板,它不是基于jQuery的设计,而是基于OOP透视(我更喜欢)。我认为它更干净,但却牺牲了不遵循通常的语法(element.myPlugin({options})并且无法链接(直到你修改一下)

同一个人有一个较旧的帖子,这是通常的jQuery插件设计的样板。

答案 1 :(得分:0)

在查看我的插件如何保存状态时,我发现了您的推文,同时学习了本教程中的插件开发: http://tutsplus.com/lesson/head-first-into-plugin-development/

  

在这个庞大的课程中,我们将深入研究jQuery插件开发。   在此过程中,我们将审查各种最佳实践和技术   为您的用户提供最高级别的灵活性   插件。

答案 2 :(得分:0)

就插件设计模式而言,我个人建议sticking to what the jQuery team recommends。它有助于保持一致性,并使您的插件更加社区友好。

说完了......

我遇到了尝试保持多元素状态的问题。我发现的一个解决方案是使用jQuery Data API(看起来像这样:$( selector ).data( key, value ))来保存元信息,如元素的状态或应用程序状态。

使用data()的好处在于它不会更新/访问DOM,而是使用jQuery的内部元素,因此访问速度比尝试存储信息隐藏输入字段,更改类名或执行其他操作更快开发人员试图用来在客户端上存储数据的时髦技巧。 (请记住,您不需要使用HTML5文档类型来使用数据API,但如果您执行data-*key attributes are extremely helpful!)

当所有元素都有自己的状态但当前元素是控制整个插件状态的元素时,它会变得棘手。对于这种情况,我使用body标签存储当前元素的数据,如下所示:

    $('body').data('myPluginNameSpace.current', selectorRef );

这样,当我需要检查我的插件/页面/应用程序的状态,或者监听我冒泡到文档对象的插件特定事件时,我可以快速查找当前/所选元素,并对其应用任何UI更改或行为:

    var currentElementRef = $('body').data('myPluginNameSpace.current');
    doFunStuff( currElementRef );

还有很多其他方法可以做到这一点,例如creating a custom Event object and attaching custom parameters to it

    var myPluginEvent = jQuery.Event( 'customEvent.myPluginNameSpace', { myProp : myValue });
    $( document ).trigger( myPluginEvent );

当您的自定义事件被触发并稍后通过回调函数处理时,您的自定义参数将附加到传递给处理程序的事件对象:

   $( document ).on( 'customEvent.myPluginNameSpace', function( e ){
      doStuff( e.myProp ); //you can access your custom properties attach to the event
    });

您可以通过许多不同的道路到达同一个目的地;这就是JavaScript的美丽和恐怖。

在您的特定情况下请记住,您不必在您的插件的methods.init函数的return this.each({ })部分内运行所有内容:

例如,除非您为每个元素设置特定选项,否则我将取出您为每个元素扩展选项对象的部分!

var methods = {
    init: function(options) {
        //DO OPTIONS/EVENTLISTENER/etc STUFF OUT HERE
        return this.each(function() {
            //DONT DO THIS
            if (options) {
                _options = $.extend($.fn.examplePlugin.defaults, options);
            } else {
                _options = $.fn.examplePlugin.defaults;
            }

请改为尝试:

...
    var methods = {

        init : function( options ){

          //do setup type stuff for the entire Plugin out here
          var _options = $.MyPlugin.options = $.extend( defaults, options );

          //add some listeners to $(document) that will later be handled               
          //but put them in an external function to keep things organized:
          //methods.addListeners() 

          //this refers to the array of elements returned by $(selector).myPlugin();
          //this.each() iterates over, EACH element, and does everything inside (similar to Array.map())
          //if the selector has 100 elements youre gonna do whats in here 100 times
          return this.each(function(){
            //do function calls for individual elements here        
          });

        },

此外,利用自定义活动将有助于您!向文档对象添加一些事件侦听器,让事件处理程序使用数据API或自定义事件参数确定要与哪个元素进行交互。