2012-06-20 23 views
0

我一直在試圖學習如何構建jQuery插件,所以我對此仍然陌生。既然這是我的第一個插件,我決定嘗試一個簡單的可摺疊面板/盒子(你只能讀得太多,對吧?)。點擊事件觸發時,我無法訪問對JavaScript類對象的引用。我知道這個事件的內部是指引發事件的元素。我也知道在事件發生前我可以做一些類似_self = this;的事情,但那隻會緩存集合中的最後一個對象。有沒有人對我如何保持對類對象的引用有任何建議?在Click事件中訪問Javascript類對象

謝謝!

這是我的代碼。

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); 
+0

這是一個確定的插件模式? –

+0

從我看過的所有不同的模式中,這是其中之一。如果你有另一種模式的建議,我很樂意嘗試。 – Kyle

+0

我使用[本jQuery教程](http://docs.jquery.com/Plugins/Authoring)中解釋的模式「最佳實踐」模式。你需要投入一些時間來學習它,但我認爲這是一個很好的投資。底部'if(methods [method]){...}'的部分總是相同的,因此您可以自由地編寫這些方法。他們沒有解釋的一點是,你也可以在方法對象外部定義私有變量和私有函數;這可能是有用的。我總是在你的模式中定義私有'var pluginName ='...'',以免在多處寫入插件名稱。 –

回答

0

我已成功使用.data()方法的組合和.call()方法,以確保函數調用的上下文來解決這個問題始終是父元素。我設法使用我使用的模式以及使用jQuery插件創作教程所建議的模式來工作。這是否是正確的做法,我不知道。

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