0

我一直在尝试学习如何构建 jQuery 插件,所以我还是新手。由于这是我的第一个插件,我决定尝试一个简单的可折叠面板/盒子(你只能阅读这么多,对吧?)。当点击事件触发时,我无法访问对我的 Javascript 类对象的引用。我知道事件内部的 this 是指触发事件的元素。我也知道我可以_self = this;在事件之前做一些事情,但这只会缓存集合中的最后一个对象。有人对我如何保留对类对象的引用有任何建议吗?

谢谢!

这是我的代码。

HTML 代码

<div class="mypanel" title="Panel 1">Testing panel 1</div>
<div class="mypanel" title="Panel 2">Testing panel 2</div>
$('.mypanel').collapsiblePanel();

插件代码

var contentVisible = 'showContent', contentNotVisible = 'hideContent';

;(function($) {
    var pluginName = 'collapsibleBox';

    function Plugin(element, options) {
        this.ele = element;
        this.$ele = $(element);
        var _self = this;

        this.options = $.extend({}, $.fn[pluginName].defaults, options);

        this.init();

        /* Expose methods of Plugin we wish to be public.
         * This gets stored when the plugin is created
         */
        return {
            option: this.option,
            destroy: this.destroy
            /* Other public methods here */
        }
    }
    $.fn[pluginName] = function(options) {
        /* If the first parameter is a string, treat this as a call to a public method. */
        if(typeof arguments[0] === 'string') {
            var methodName = arguments[0];
            var args = Array.prototype.slice.call(arguments, 1);
            var returnVal;
            this.each(function() {
                /* Check that the element has a plugin instance, and that
                 * the requrested public method exists.
                 */
                if($.data(this, 'plugin_' + pluginName) && typeof $.data(this, 'plugin_' + pluginName)[methodName] === 'function') {
                    /* Call the method of the Plugin instance, and Pass it the supplied arguments */
                    returnVal = $.data(this, 'plugin_' + pluginName)[methodName].appy(this, args);
                }
                else {
                    $.error('Method ' + methodName + ' does not exist on jQuery.' + pluginName);
                }
            });
            if(returnVal !== undefined) {
                /* If the method returned something, return it */
                return returnVal;
            }
            else {
                /* Otherwise, returning 'this' preserves chainability */
                return this;
            }
        }
        /* If the first parameter is an object (options), or was omitted,
         * instantiate a new instance of the plugin
         */
        else if(typeof options === 'object' || !options) {
            return this.each(function() {
                /* Only allow the plugin to be instantiated once */
                if(!$.data(this, 'plugin_' + pluginName)) {
                    /* Pass options to Plugin constructor, and store Plugin
                     * instance in the element's jQuery data object
                     */
                    $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
                }
            });
        }
    };

    $.fn[pluginName].defaults = {
        onInit: function() {},
        onDestroy: function() {}
    };

    Plugin.prototype = {
        init: function() {
            this.createContentArea();
            this.createTitleBar();
            this.hook('onInit');
        },
        createContentArea: function() {
            /* ... */
        },
        createTitleBar: function() {
            /* ... */

            this.$title.click(function() {
                if(this.$ele.data('state') == contentVisible) { // The problem is here
                    this.collapse();
                }
                else {
                    this.expand();
                }
            });
        },
        expand: function() {
            this.$content.slideDown();
            this.$ele.data('state', contentVisible);
        },
        collapse: function() {
            console.log(this);
            this.$content.slideUp();
            this.$ele.data('state', contentNotVisible);
        },
        /* Use this function to get/set variables */
        option: function (key, val) {
            if(val) {
                this.options[key] = val;
            }
            else {
                return this.options[key];
            }
        },
        destroy: function () {
            /* ... */
        },

        hook: function (hookName) {
            if(this.options[hookName] !== undefined) {
                this.options[hookName].call(this.ele);
            }
        }
    };
})(jQuery);
4

1 回答 1

0

我已经设法使用.data()方法和.call()方法的组合来解决这个问题,以确保函数调用的上下文始终是父元素。我设法使用我拥有的模式以及使用 jQuery Plugin Authoring Tutorial 建议的模式来完成这项工作。这是否是正确的做法,我不知道。

var methods = {
    init: function(options) {
        var $this = $(this), data = {};
        /* Set a local copy of the options */
        data.options = $.extend({}, defaults, options);
        /* Create the content area */
        data.$content = createContentArea(this);
        /* Create the title bar */
        data.$title = createTitleBar(this, data.options.header, data.options.title);

        /* Show/Hide the content based on the default state passed in */
        if(data.options.defaultState == contentVisible) {
            data.$content.show();
            data.state = contentVisible;
        }
        else {
            data.$content.hide();
            data.state = contentNotVisible;
        }

        /* Add the title click event */
        data.$title.click(function() {
            methods.toggle.call($(this).parent());
        });

        /* Add the public methods */
        data.methods = {
            toggle: methods.toggle, 
            expand: methods.expand, 
            collapse: methods.collapse
        };
        return data;
    }, 
    toggle: function() {
        var $this = $(this), data = $this.data('plugin_' + pluginName);
        if(data.state == contentVisible) {
            methods.collapse.call(this);
        }
        else if(data.state == contentNotVisible) {
            methods.expand.call(this);
        }
    }, 
    expand: function() {
        var $this = $(this), data = $this.data('plugin_' + pluginName);
        data.$content.slideDown();
        data.state = contentVisible;
        $this.data('plugin_' + pluginName, data);
    }, 
    collapse: function() {
        var $this = $(this), data = $this.data('plugin_' + pluginName);
        data.$content.slideUp();
        data.state = contentNotVisible;
        $this.data('plugin_' + pluginName, data);
    }
};
于 2012-06-21T12:20:28.740 回答