【问题标题】:Can't destroy jQuery plugin无法销毁 jQuery 插件
【发布时间】:2014-09-18 14:24:35
【问题描述】:

我一直在为一个项目开发一些 javascript,并决定它应该是一个 jQuery 插件。我以前写过一些,但这需要更加健壮和可破坏。为此,我遵循了一些教程,但在描述如何销毁插件时,它们都不够完善。

那么我该如何销毁插件呢?我似乎无法访问$('.js-target).fullscreen('destroy') 似乎不起作用。 $(.js-target).data('fullscreen').destroy() 也不会在控制台中返回 TypeError: Cannot read property 'destroy' of undefined

我用coffeescript 写的。生成的 javascript 贴在下面。

(($, window) ->

  'use strict'

  # Create the defaults once
  pluginName = 'fullscreen'
  defaults =
    reference: window
    offset: 0
    debug: true

  # The actual plugin constructor
  Plugin = ( element, options ) ->
    this.element = element
    this.options = $.extend {}, defaults, options
    this._defaults = defaults
    this._name = pluginName
    this.init()

  Plugin.prototype.init = ->

    this.bind()
    this.setHeight()

  Plugin.prototype.bind = ->

    # Maintain the scope
    self = this

    # Trigger on resize
    $(window).on 'resize orientationchange', ->
      self.setHeight()

    # When scrolling on a touchscreen
    # prevent further resizes due to address bar shrinking
    $(window).on 'touchstart', ->
      self.unbind()

  Plugin.prototype.getHeight = ->

    this.log 'Get height from: ', this.options.reference
    $( this.options.reference ).height()

  Plugin.prototype.setHeight = ->

    if this.options.offset == parseInt( this.options.offset )
      offset = this.options.offset
    else
      offset = 0

    $(this.element).css
      'min-height' : this.getHeight() - offset

  Plugin.prototype.unbind = ->

    this.log 'Unbind the resize, touchstart and orientationchange event handlers'
    $(window).off 'resize touchstart orientationchange'

  Plugin.prototype.destroy = ->

    this.unbind()

    log 'Remove any heights set on', this.element
    $(this.element).attr('style','')

  Plugin.prototype.log = ( msg, object ) ->
    if this.options.debug
      if !object
        object = ''
      console.log( pluginName + ': ' + msg, object )

  # A really lightweight plugin wrapper around the constructor,
  # preventing multiple instantiations
  $.fn[pluginName] = ( options ) ->
    return this.each ->
      if !$.data(this, 'plugin_' + pluginName)
        $.data(this, 'plugin_' + pluginName)
        new Plugin(this, options)

  return $.fn[pluginName]

) jQuery, window

这是生成的 javascript。会不会是coffeescript封装的匿名函数?

  (function(){
    (function($, window) {
      'use strict';
      var Plugin, defaults, pluginName;
      pluginName = 'fullscreen';
      defaults = {
        reference: window,
        offset: 0,
        debug: true
      };
      Plugin = function(element, options) {
        this.element = element;
        this.options = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;
        return this.init();
      };
      Plugin.prototype.init = function() {
        this.bind();
        return this.setHeight();
      };
      Plugin.prototype.bind = function() {
        var self;
        self = this;
        $(window).on('resize orientationchange', function() {
          return self.setHeight();
        });
        return $(window).on('touchstart', function() {
          return self.unbind();
        });
      };
      Plugin.prototype.getHeight = function() {
        this.log('Get height from: ', this.options.reference);
        return $(this.options.reference).height();
      };
      Plugin.prototype.setHeight = function() {
        var offset;
        if (this.options.offset === parseInt(this.options.offset)) {
          offset = this.options.offset;
        } else {
          offset = 0;
        }
        return $(this.element).css({
          'min-height': this.getHeight() - offset
        });
      };
      Plugin.prototype.unbind = function() {
        this.log('Unbind the resize, touchstart and orientationchange event handlers');
        return $(window).off('resize touchstart orientationchange');
      };
      Plugin.prototype.destroy = function() {
        this.unbind();
        log('Remove any heights set on', this.element);
        return $(this.element).attr('style', '');
      };
      Plugin.prototype.log = function(msg, object) {
        if (this.options.debug) {
          if (!object) {
            object = '';
          }
          return console.log(pluginName + ': ' + msg, object);
        }
      };
      $.fn[pluginName] = function(options) {
        return this.each(function() {
          if (!$.data(this, 'plugin_' + pluginName)) {
            $.data(this, 'plugin_' + pluginName);
            return new Plugin(this, options);
          }
        });
      };
      return $.fn[pluginName];
    })(jQuery, window);
  }).call(this);

任何帮助将不胜感激。

【问题讨论】:

    标签: jquery jquery-plugins coffeescript destroy


    【解决方案1】:

    这里发生了一些奇怪的事情,所以我将从顶部开始。

    CoffeeScript 看起来像是将现有的 jQuery 插件从 JavaScript 音译为 CoffeeScript。你应该在 CoffeeScript 中编写 CoffeeScript:

    class Plugin
      constructor: (@element, options) ->
        @options = $.extend { }, defaults, options
        #...
        @init()
      init: ->
        @bind()
        @setHeight() # The `return` is implicit here
      bind: ->
        # Use `=>` instead of an explicit `self = this` trick.
        $(window).on 'resize orientationchange', => @setHeight()
        $(window).on 'touchstart', => @unbind()
      #...
    

    现在是实际的插件定义:

    $.fn[pluginName] = ( options ) ->
      return this.each ->
        if !$.data(this, 'plugin_' + pluginName)
          $.data(this, 'plugin_' + pluginName)
          new Plugin(this, options)
    

    if 中的$.data call 没有任何用处,如果您的意图是将Plugin 实例附加到DOM 节点,则需要$.data(obj, key, value) 形式的$.data。同样,您不需要 returns 和 @ 在 CoffeeScript 中比 this 更常见:

    $.fn[pluginName] = (options) ->
      @each ->
        if !$.data(@, "plugin_#{pluginName}")
          $.data(@, "plugin_#{pluginName}", new Plugin(@, options))
    

    对于$.data 键,我还切换到字符串插值而不是+,这通常更容易阅读。

    现在你应该可以说:

    $('.js-target').data('plugin_fullscreen').destroy()
    

    请注意,数据键是'plugin_fullscreen',而不是'fullscreen'。这当然有点恶心,你可能不想强迫每个人都看私人细节。

    如果你想做 jQuery-UI 风格的事情,比如:

    $('.js-target').fullscreen('destroy')
    

    那么您需要做的就是更新插件函数以知道'destroy' 应该是一个方法调用而不是options 对象。像这样简单的事情应该可以帮助您入门:

    $.fn[pluginName] = (args...) ->
      @each ->
        plugin = $.data(@, dataKey)
        if typeof args[0] == 'string'
          plugin?[args[0]]?()
        else if !plugin
          $.data(@, dataKey, new Plugin(@, args[0]))
    

    因此,如果您说$(x).fullscreen('string'),那么它假定您正在尝试调用内部Plugin 实例上的方法,所有存在运算符(?)只处理缺失值(未附加插件,未知方法, ...);在现实生活中,您可能希望将允许以这种方式调用的方法列入白名单。如果您说$(x).fullscreen(opt: 1),那么它假定您正在尝试将插件附加到使用{opt: 1} 作为选项的东西。同样,现实生活中的版本可能会更复杂。

    Quick'n'dirty 演示:http://jsfiddle.net/ambiguous/568SU/1/

    如果你经常做这类事情,你可能想看看jQuery-UI widget factory,工厂会为你处理很多不愉快的细节。

    【讨论】:

    • 这是一个非常详细的答案。感谢您花时间解释它为什么不起作用,而不仅仅是告诉我它应该如何工作。我对 CoffeeScript 的细微差别不熟悉,不知道您提到的巧妙技巧。
    • 让我知道是否有任何技巧(例如 args...=> 可能)令人困惑,如果您愿意,我可以向您指出官方文档的部分内容。我明天下午晚些时候回来。
    • 现在我知道它们存在,我在文档中找到了它们。一直在学习!再次感谢。
    猜你喜欢
    • 2020-05-05
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    • 2015-12-04
    • 2014-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多