【问题标题】:JQuery plugin not working when used in multiple places in a single page在单个页面中的多个位置使用时,JQuery 插件不起作用
【发布时间】:2016-03-13 15:15:26
【问题描述】:

我正在为我正在处理的项目编写一个 JQuery 插件,该插件从桌面设备上的选项卡式内容转变为移动设备上的手风琴。我使用 JQuery Boilerplate (https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/dist/jquery.boilerplate.js) 作为我插件的初始模式。

插件在任何具有“.tabs2accordion”类的元素上调用,如下所示:

 $(".tabs2accordion").tabs2Accordion({state:"desktop"});

如果页面上只有一个具有“.tabs2accordion”类的元素,插件将按预期工作,但一旦将具有相同类的另一个元素添加到页面,该插件就会开始出现故障。我创建了一个基本代码的代码笔来演示这个问题。要显示此问题,请在 >768px 的窗口大小上尝试单击任何标题,并观察单击每个标题时下面的内容如何变化。接下来取消注释 HTML 块并尝试再次单击标题。

http://codepen.io/decodedcreative/pen/MyjpRj

我尝试使用“tabs2accordion”类循环遍历每个元素,如下所示:

$(".tabs2accordion").each(function(){
    $(this).tabs2Accordion({state:"desktop"});
});

但这也没有解决问题。

有什么想法吗?

【问题讨论】:

  • 但是一旦将另一个具有相同类的元素添加到页面中就会开始出现故障 ....怎么会?你只是模糊地解释了你的代码应该做什么,根本没有解释实际问题是什么,除了说有问题。请更具体
  • 在 768px 及以上的屏幕尺寸上,点击三个标题之一将显示其对应的标签内容段落。单击另一个标题将显示另一个选项卡内容段落。我可能没有很好地描述它,但我确实提供了一个 Codepen 来演示这个问题,所以没有理由粗鲁
  • 我很抱歉我的粗鲁,我只是想让你看看你没有给我们提供帮助你所需的信息。我们都很乐意为您提供帮助,但在我们深入研究您的示例中的代码之前,我们需要知道代码应该做什么以及代码当前如何失败。这些是对于外人来说,通常不像对提问者那样明显。例如,当我在您的演示中缩小屏幕时,单击标题时没有任何反应,请参阅i.imgur.com/v0JPO2g.png
  • 好的,我已经编辑了原始帖子,以进一步解释如何在 Codepen 中显示问题。我没有包含手风琴功能的 JS,因为它涉及使用第三方库,我不想使示例过于复杂。当在 >768px 的窗口上查看时,上面的 Codepen 与我的项目有相同的问题。我怀疑插件的两个实例不是完全独立的,因此当页面上有多个实例时,单击一个插件实例的标题会影响另一个插件实例的 HTML。

标签: javascript jquery jquery-plugins jquery-boilerplate


【解决方案1】:

我没有使用过 jQuery Boilerplate,但我相信这里的问题在于您的名为 plugin 的变量。

您的代码中没有任何地方声明一个名为plugin 的变量。当我在Plugin.prototype.showTabContent 中停止调试器时,我可以评估window.plugin 并返回插件的全局值。

在 Plugin 的构造函数中,第一行为plugin= this;。由于plugin 没有定义,它在window 对象的全局范围内声明变量。

修复方法是在设置 $().on() 挂钩时传递对 plugin 对象的引用。传递的数据可通过在data 属性中传递的event 参数在事件处理程序中使用。

这是解决方案(http://codepen.io/shhQuiet/pen/JXEjMV

(function($, window, document, undefined) {
  var pluginName = "tabs2Accordion",
    defaults = {
      menuSelector: ".tabs2accordion-menu",
      tabContentSelector: ".tabs2accordion-content"
    };

  function Plugin(element, options) {
    this.element = element;
    this.$element = $(this.element);
    this.options = $.extend({}, defaults, options);
    this.$menu = $(this.element).find(this.options.menuSelector),
    this.$tabs = $(this.element).find(this.options.tabContentSelector),
    this.$accordionTriggers = $(this.element).find(this.$tabs).find("h3");
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

  Plugin.prototype = {

    init: function() {
      //Set all the tab states to inactive
      this.$tabs.attr("data-active", false);

      //Set the first tab to active
      this.$tabs.first().attr("data-active", true);

      //If you click on a tab, show the corresponding content
      this.$menu.on("click", "li", this, this.showTabContent);

      //Set the dimensions (height) of the plugin
      this.resizeTabs2Accordion({
        data: this
      });

      //If the browser resizes, adjust the dimensions (height) of the plugin
      $(window).on("resize", this, this.resizeTabs2Accordion);

      //Add a loaded class to the plugin which will fade in the plugin's content
      this.$element.addClass("loaded");

      console.log(this.$element);

    },

    resizeTabs2Accordion: function(event) {
      var contentHeight;
      var plugin = event.data;

      if (!plugin.$element.is("[data-nested-menu]")) {
        contentHeight = plugin.$tabs.filter("[data-active='true']").outerHeight() + plugin.$menu.outerHeight();
      } else {
        contentHeight = plugin.$tabs.filter("[data-active='true']").outerHeight();
      }

      plugin.$element.outerHeight(contentHeight);
    },

    showTabContent: function(event) {
      var $target;
      var plugin = event.data;
      plugin.$menu.children().find("a").filter("[data-active='true']").attr("data-active", false);
      plugin.$tabs.filter("[data-active='true']").attr("data-active", false);
      $target = $($(this).children("a").attr("href"));
      $(this).children("a").attr("data-active", true);
      $target.attr("data-active", true);
      plugin.resizeTabs2Accordion({data: plugin});

      return false;
    },

    showAccordionContent: function(event) {
      var plugin = event.data;
      $("[data-active-mobile]").not($(this).parent()).attr("data-active-mobile", false);

      if ($(this).parent().attr("data-active-mobile") === "false") {
        $(this).parent().attr("data-active-mobile", true);
      } else {
        $(this).parent().attr("data-active-mobile", false);
      }
    }

  };

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (!$.data(this, "plugin_" + pluginName)) {
        $.data(this, "plugin_" + pluginName, new Plugin(this, options));
      }
    });
  };

})(jQuery, window, document);

$(window).on("load", function() {
  $(".tabs2accordion").tabs2Accordion({
    state: "desktop"
  });
});

【讨论】:

  • 我使用 'plugin=this' 将 'this' 关键字的上下文从 init 函数传递到插件中的其他函数,例如 showTabContent。否则我将无法使用函数插件中定义的变量,因为“this”关键字的上下文会发生变化。
  • 我看到了,但是这是失败的根本原因。在初始化插件的第二个实例时,您正在使用全局变量并覆盖该值。你需要找到另一种方法。
  • 感谢您的帮助。你知道另一种方法可以传递插件实例的上下文,以便我可以将变量自包含到每个插件实例吗?
  • 今天晚些时候我会看看并更改我的答案以反映解决方案。
  • 谢谢。这不仅可以帮助我,还可以帮助许多其他开发人员使用这种模式编写他们的第一个插件。
【解决方案2】:

我按照 jQuery 的插件创建标准重写了您的代码。

http://codepen.io/justinledouxmusique/pen/GZrMgB

基本上,我做了两件事:

  • 不再使用数据属性进行样式设置(改用.active 类)
  • 不再到处使用this,因为它会带来一整波绑定问题...

$.fn.tabs2Accordion 循环遍历所有选择器,并应用$.tabs2Accordion。它还返回用于链接的选择器(这是 jQuery 中的标准)。

然后,所有内部方法都是函数表达式,它们与所有旧的this“变量”在同一范围内。这极大地简化了代码,因为您可以引用这些变量而无需将它们作为参数传递或不必以某种方式.bind( this )

最后,旧的init() 功能消失了。相反,我将代码放在$.tabs2Accordion 函数的末尾。

希望这会有所帮助!

(function ( window, $ ) {
    $.tabs2Accordion = function ( node, options ) {
        var options = $.extend({}, {
                    menuSelector: '.tabs2accordion-menu',
                    tabContentSelector: '.tabs2accordion-content'
                }, options )

        var $element = $( node ),
                $menu = $element.find( options.menuSelector ),
                $tabs = $element.find( options.tabContentSelector ),
                $accordionTriggers = $tabs.find( 'h3' )

        var resizeTabs2Accordion = function () {
            $element.outerHeight( !$element.is( '[data-nested-menu]' )
                ? $element.find( 'div.active' ).outerHeight() + $menu.outerHeight()
                : $element.find( 'div.active' ).outerHeight() )
        }

        var showTabContent = function () {
            var $this = $( this ) // This will be the clicked element

            $menu
                .find( '.active' )
                    .removeClass( 'active' )

            $element
                .find( '.active' )
                    .removeClass( 'active' )

            $( $this.find( 'a' ).attr( 'href' ) )
                .addClass( 'active' )

            $this
                .find( 'a' )
                    .addClass( 'active' )

            resizeTabs2Accordion()

            return false
        }

        var showAccordionContent = function () {
            var $this                   = $( this ),
                    $parent                 = $this.parent(),
                    mobileIsActive  = $parent.data( 'active-mobile' )

            $( '[data-active-mobile]' )
                .not( $parent )
                    .data( 'active-mobile', false )

            $parent
                .data( 'active-mobile', mobileIsActive ? false : true )
        }

        // The equivalent of init()
        $tabs
            .removeClass( 'active' )
            .first()
                .addClass( 'active' )

        $element.addClass( 'loaded' )

        $menu.on( 'click', 'li', showTabContent )

        $( window ).on( 'resize', resizeTabs2Accordion )

        resizeTabs2Accordion()

        console.log( $element )
    }

    $.fn.tabs2Accordion = function ( options ) {
        this.each( function ( index, node ) {
            $.tabs2Accordion( node, options )
        })

        return this
    }
})( window, jQuery )

$( window ).on( 'load', function () {
    $( '.tabs2accordion' ).tabs2Accordion({
        state: 'desktop'
    })
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多