简体   繁体   English

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

[英]Is there a better way/pattern to write this jQuery plugin?

I have a search plugin that is decently complex: it has different versions of UI and functionality as well as a bunch in interdependent domElements. 我有一个相当复杂的搜索插件:它具有不同版本的UI和功能,以及相互依赖的domElements中的一堆。 Multiple instances of the plugin will exist on a page at once. 插件的多个实例将立即存在于页面上。

I am using the basic jQuery authoring pattern: http://docs.jquery.com/Plugins/Authoring 我正在使用基本的jQuery创作模式: http//docs.jquery.com/Plugins/Authoring

In order to save the options, interdependent events and and all sorts of dom lookups across multiple objects, I've come to passing the element in question to every function, and storing state/options/interdependencies in a data attribute which I retrieve each time. 为了保存选项,相互依赖的事件以及跨多个对象的各种dom查找,我将有问题的元素传递给每个函数,并将状态/选项/相互依赖性存储在我每次检索的数据属性中。 It works, and keeps events from colliding, but it seems like a messy way to write code. 它可以工作,并防止事件发生冲突,但编写代码似乎是一种混乱的方式。

What is the best way to store state across multiple instances? 跨多个实例存储状态的最佳方法是什么? Is the way I am doing it a huge overkill and I am missing something? 我这样做的方式是一个巨大的矫枉过正,我错过了什么? It probably stems from my misunderstanding of creating class like objects in a jQuery plugin pattern. 它可能源于我在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);

Yup, if you follow the jQuery guide, you are building it according to how it's supposed to be built and taking advantage of what it was designed to do (especially chaining). 是的,如果你按照jQuery指南,你正在构建它根据它应该如何构建并利用它的设计目的(特别是链接)。

However, I don't necessarily follow that path. 但是,我不一定遵循这条道路。 There are a lot of ways you can do these plugins, take for example this guy who made a boilerplate for jQuery plugins which are NOT based on jQuery's design but rather in the OOP perspective (which I prefer). 你可以通过很多方法来完成这些插件, 比如这个为jQuery插件制作样板的人,它不是基于jQuery的设计,而是基于OOP的角度(我更喜欢)。 I see it as cleaner, but has the sacrifice of not following the usual syntax (the element.myPlugin({options}) and not being able to chain (until you modify a bit) 我认为它更干净,但是牺牲了不遵循通常的语法( element.myPlugin({options})并且无法链接(直到你修改了一下)

The same guy has an older post which is a boilerplate for the usual jQuery plugin design. 同一个人有一个较旧的帖子,这是通常的jQuery插件设计的样板。

Personally, I suggest sticking to what the jQuery team recommends , in terms of plugin design patterns. 就个人而言,我建议坚持jQuery团队推荐的插件设计模式。 It helps keeps consistency, and makes your plugin more community friendly. 它有助于保持一致性,并使您的插件更加社区友好。

Having said that... 话说回来...

I've run into the problem of trying to keep the state of multiple elements as well. 我遇到了试图保持多个元素状态的问题。 One solution I've found is to use the jQuery Data API (which looks like this: $( selector ).data( key, value ) ) to keep meta information like an element's state or the application state. 我发现的一个解决方案是使用jQuery Data API (看起来像这样: $( selector ).data( key, value ) )来保存元信息,如元素的状态或应用程序状态。

The nice thing about using data() is that it's not updating/acessing the DOM, rather it's using jQuery's internal meta stuff, so it's faster to access than trying to store info hidden input fields, changing class names, or doing other funky tricks that developers have tried to use to store data on the clientside. 使用data()的好处在于它不会更新/访问DOM,而是使用jQuery的内部元素,因此访问速度比尝试存储信息隐藏输入字段,更改类名或执行其他时髦技巧更快开发人员试图用来在客户端上存储数据。 ( Keep in mind too that you don't need to use the HTML5 doctype to use the data API, but if you do data-*key attributes are extremely helpful ! ) (请记住,您不需要使用HTML5文档类型来使用数据API,但如果您执行数据 - *键属性非常有帮助 !)

It gets tricky when all the elements have their own states but the current element is the one that is controlling the overall plugin state. 当所有元素都有自己的状态但当前元素是控制整个插件状态的元素时,它会变得棘手。 For this scenario I use the body tag to store data bout the current element , something like this: 对于这种情况,我使用body标签来存储当前元素的数据 ,如下所示:

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

That way, when I need to check the state of my plugin/page/application, or listen for my plugin-specific event that's bubbled up to the document object, I can do a quick lookup for the current/selected element, and apply any UI changes or behaviors to it: 这样,当我需要检查我的插件/页面/应用程序的状态,或者监听我冒泡到文档对象的插件特定事件时,我可以快速查找当前/所选元素,并应用任何UI更改或行为:

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

There are a number of other ways you can do this too, like creating a custom Event object and attaching custom parameters to it : 您还可以通过许多其他方式执行此操作,例如创建自定义Event对象并将自定义参数附加到其中

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

When your custom Event gets triggered and later handled via a callback function, your custom parameters are attached to the Event Object passed to the handler: 当您的自定义事件被触发并稍后通过回调函数处理时,您的自定义参数将附加到传递给处理程序的事件对象:

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

You can get to the same destination via many, different roads; 您可以通过许多不同的道路到达同一个目的地; that's the beauty and horror of JavaScript. 这就是JavaScript的美丽和恐怖。

In your particular case keep in mind that you don't have to have everything running inside return this.each({ }) portion of the methods.init function for your plugin: 在您的特定情况下请记住,您不必让所有内部运行都return this.each({ })您的插件的methods.init函数的return this.each({ })部分:

For example, unless you are setting specific options for each element, I would take out the part where you're extending the options object for every element! 例如,除非您为每个元素设置特定选项,否则我将取出您为每个元素扩展选项对象的部分!

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;
            }

Try this instead: 试试这个:

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

        },

Also, taking advantage of custom events will help you! 此外,利用自定义事件将帮助您! Add some event listeners to the document object, and let the event handlers figure out which element to interact with using the data API or custom event parameters. 向文档对象添加一些事件侦听器,让事件处理程序使用数据API或自定义事件参数确定要与哪个元素进行交互。

I've found your tweet when checking how my plugin saves a state, while learning plugin developing along this tutorial: http://tutsplus.com/lesson/head-first-into-plugin-development/ 我在查看我的插件如何保存状态时发现了你的推文,同时学习了本教程中的插件开发: http//tutsplus.com/lesson/head-first-into-plugin-development/

In this massive lesson, we'll dive into jQuery plugin development. 在这个庞大的课程中,我们将深入研究jQuery插件开发。 Along the way, we'll review various best practices and techniques for providing the highest level of flexibility for the users of your plugins. 在此过程中,我们将审查各种最佳实践和技术,以便为插件用户提供最高级别的灵活性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM