创建两个可以相互通信的 jQuery 插件

Create two jQuery plugins that can talk to each other

我的脑袋现在一团糟。我想创建两个 jQuery 插件:

  1. 一个选项卡插件
  2. 滑块插件。

此插件必须相互通信,例如,如果我单击一个选项卡,此选项卡将激活相关滑块中的正确索引。 如果我单击幻灯片,这也会激活正确的选项卡。

我开始为此创建事件侦听器和触发器,当我的选项卡被单击时,一个事件 tabChanged 被触发,并且在我的滑块插件中我正在收听它。 但问题是,我的滑块可以在我的选项卡之前加载,所以听众没有正确附加...

这次我尝试在文档上触发另一个事件,称为 tabsLoaded 并等待该响应,它有效但开始有点混乱。

我想知道是否有人对此有更好的解决方案? 选项卡和滑块也可以独立工作,可能只有选项卡没有关联的滑块。

这是我的标签插件:

(function($) {
  let pluginName = 'Tabs';

  function Tabs(element, options) {
    let settings = {};

    this.element = element;
    this.$element = $(this.element);
    this.settings = $.extend({}, settings, options);

    this.children = null;
    this.activeTabIndex = 0;
    this.nbTabs = 0;

    this.init();
  }

  $.extend(Tabs.prototype, {

    init: function() {
      this.children = this.$element.children();
      this.nbTabs = this.children.length;

      // Listeners
      this.children.on('click', {
        tabs: this
      }, this.onTabChange); // Click on a tab
      $(this).on('tabChange', this.setActive);

      // On Init, active the first tab
      if (this.children && this.nbTabs > 0) {
        $(this).trigger({
          type: 'tabChange',
          tab: this.children.first().index(),
        });
      }

      $(document).trigger({
        type: 'tabLoaded',
        tabs: this,
      });
    },
    setActive: function(event) {
      this.activeTabIndex = event.tab;
      this.children.eq(this.activeTabIndex).addClass('is-active');
    },
    onTabChange: function(event) {
      event.preventDefault();
      const tabs = event.data.tabs;
      // Reset active classes
      tabs.children.removeClass('is-active');
      // Launch changeTab
      $(tabs).trigger({
        type: 'tabChange',
        tab: tabs.children.index(event.currentTarget),
      });
    }
  });

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (!$.data(this, pluginName)) {
        $.data(this, pluginName, new Tabs(this, options));
      }
    });
  };
})(jQuery);

jQuery(document).ready(function($) {
  $('.js-tabs').Tabs();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

这是我的监听标签的滑块插件:

(function($) {
  let pluginName = 'Slider';

  function Slider(element, options) {
    let settings = {};

    this.element = element;
    this.$element = $(this.element);
    this.settings = $.extend({}, settings, options);

    this.id = null;
    this.tabs = null;
    this.children = null;
    this.activeSlideIndex = 0;
    this.nbSlides = 0;

    this.init();
  }

  $.extend(Slider.prototype, {

    init: function() {
      this.id = this.$element.attr('id');
      this.children = this.$element.children();
      this.nbSlides = this.children.length;
      // Listeners
      // Click on slide
      this.children.on('click', {
        slider: this
      }, this.onSlideChange);
      // On slide change
      $(this).on('slideChange', {
        slider: this
      }, this.onSlideChange);
      $(this).on('change', this.update);

      // On Init, active the first tab
      if (this.children && this.nbSlides > 0) {
        $(this).trigger({
          type: 'slideChange',
          slide: this.children.first().index(),
        });
      }
      $(document).trigger({
        type: 'sliderLoaded',
        slider: this,
      });
    },
    update: function() {
      // if Slider has an associated Tabs
      if (this.tabs) {
        $(this.tabs).on('tabChange', {
          slider: this
        }, this.onTabChange);
      }
    },
    onSlideChange: function(event) {
      event.preventDefault();
      const slider = event.data.slider;
      const slide = event.slide;
      // Reset active classes
      slider.children.removeClass('is-active');
      slider.activeSlideIndex = slide ? slide : event.currentTarget;
      console.log(slider.activeSlideIndex);
      slider.children.eq(slider.activeSlideIndex).addClass('is-active');
    },
    // TABS
    onTabChange: function(event) {
      const slider = event.data.slider;
      const tabIndex = event.tab;
      if ($(slider.children).eq(tabIndex).length >= 0) {
        $(slider).trigger({
          type: 'slideChange',
          slide: tabIndex,
        });
      }
    }
  });

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (!$.data(this, pluginName)) {
        $.data(this, pluginName, new Slider(this, options));
      }
    });
  };
})(jQuery);

jQuery(document).ready(function($) {
  $('.js-slider').Slider();
});

// On Tabs loaded, insert it into slider
jQuery(document).on('tabLoaded', function(event) {
  const tabs = event.tabs;
  const sliderId = jQuery(tabs.element).data('slider-id');
  if (jQuery('#' + sliderId).first().data('Slider')) {
    const slider = jQuery('#' + sliderId).first().data('Slider');
    slider.tabs = tabs;
    slider.update();
  }
});

我听从了@Anton 的建议,拆分了选项卡和滑块,效果很好:)

  • 首先,我从插件文件中删除了我的插件初始化。
  • 然后我将每个 init 放入另一个我称为 "app.js"
  • 的文件中

我的 App.js 看起来像这样:

jQuery(document).ready(function ($) {
    let $sliders = $('.js-slider').Slider();
    let $tabs = $('.js-tabs').Tabs();

    $sliders.on('slideChanged', function (event) {
        const sliderId = $(this).attr('id');
        const slide = event.slide;
        $tabs.filter('[data-slider-id=' + sliderId + ']').data('Tabs').select(slide);
    });

    $tabs.on('tabChanged', function (event) {
        const sliderId = $(this).data('slider-id');
        const tab = event.tab;
        $sliders.filter('#' + sliderId).data('Slider').select(tab);
    });
});

我正在监听像 'slideChanged' 或 'tabChanged' 这样发送的每个事件,然后我通过匹配的 ID 获得正确的选项卡或滑块实例。

这是我的标签代码:

(function($) {
  let pluginName = 'Tabs';

  function Tabs(element, options) {
    let settings = {};

    this.element = element;
    this.$element = $(this.element);
    this.settings = $.extend({}, settings, options);

    this.children = null;
    this.activeTabIndex = 0;
    this.nbTabs = 0;

    this.init();
  }

  $.extend(Tabs.prototype, {

    init: function() {
      this.children = this.$element.children();
      this.nbTabs = this.children.length;

      // Listeners
      this.children.on('click', {
        tabs: this
      }, this.onTabClick); // Click on a tab
      $(this).on('tabChanged', this.onTabClick);

      // On Init, active the first tab
      if (this.children && this.nbTabs > 0) {
        this.children.first().click();
      }
    },
    select: function(tab) {
      // Reset active classes
      this.children.removeClass('is-active');
      this.activeTabIndex = tab;
      this.children.eq(tab).addClass('is-active');
    },
    onTabClick: function(event) {
      event.preventDefault();
      const tabs = event.data.tabs;
      const tab = tabs.children.index(event.currentTarget);
      tabs.select(tab);
      // Launch changeTab
      $(tabs.element).trigger({
        type: 'tabChanged',
        tab: tab,
      });
    }
  });

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (!$.data(this, pluginName)) {
        $.data(this, pluginName, new Tabs(this, options));
      }
    });
  };
})(jQuery);

和滑块:

(function($) {
  let pluginName = 'Slider';

  function Slider(element, options) {
    let settings = {};

    this.element = element;
    this.$element = $(this.element);
    this.settings = $.extend({}, settings, options);

    this.id = null;
    this.tabs = null;
    this.children = null;
    this.activeSlideIndex = 0;
    this.nbSlides = 0;

    this.init();
  }

  $.extend(Slider.prototype, {

    init: function() {
      this.id = this.$element.attr('id');
      this.children = this.$element.children();
      this.nbSlides = this.children.length;
      // Listeners
      // Click on slide
      this.children.on('click', {
        slider: this
      }, this.onSlideClick);
      // On slide change
      $(this).on('slideChanged', this.onSlideClick);

      // On Init, active the first tab
      if (this.children && this.nbSlides > 0) {
        this.children.first().click();
      }
    },
    select: function(slide) {
      // Reset active classes
      this.children.removeClass('is-active');
      this.activeSlideIndex = slide;
      this.children.eq(this.activeSlideIndex).addClass('is-active');
    },
    onSlideClick: function(event) {
      event.preventDefault();
      const slider = event.data.slider;
      const slide = slider.children.index(event.currentTarget);
      slider.select(slide);
      // Launch changeTab
      $(slider.element).trigger({
        type: 'slideChanged',
        slide: slide,
      });
    },
  });

  $.fn[pluginName] = function(options) {
    return this.each(function() {
      if (!$.data(this, pluginName)) {
        $.data(this, pluginName, new Slider(this, options));
      }
    });
  };
})(jQuery);