2016-03-13 23 views
8

Tôi đang viết một plugin JQuery cho một dự án mà tôi đang làm việc, từ đó chuyển từ nội dung tab trên thiết bị máy tính sang accordion trên thiết bị di động. Tôi đã sử dụng JQuery Boilerplate (https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/dist/jquery.boilerplate.js) làm mẫu ban đầu cho plugin của mình.Plugin JQuery không hoạt động khi được sử dụng ở nhiều nơi trong một trang

Plugin được gọi vào bất kỳ phần tử với lớp ".tabs2accordion" như thể hiện ở đây:

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

Plugin làm việc như mong đợi nếu chỉ có một phần tử với class ".tabs2accordion" trên một trang nhưng bắt đầu trục trặc ngay khi phần tử khác có cùng lớp được thêm vào trang. Tôi đã tạo một codepen của mã cơ bản để giới thiệu vấn đề. Để hiển thị sự cố, kích thước cửa sổ> 768px hãy thử nhấp vào bất kỳ tiêu đề nào và quan sát cách nội dung bên dưới thay đổi khi mỗi tiêu đề được nhấp vào. Tiếp theo, bỏ ghi chú khối HTML và thử nhấp lại vào tựa đề.

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

Tôi đã cố gắng lặp qua từng phần tử với class "tabs2accordion" như thế này:

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

Nhưng điều này không khắc phục được vấn đề này một trong hai.

Bất kỳ ý tưởng nào?

+0

* nhưng bắt đầu trục trặc ngay khi phần tử khác có cùng lớp được thêm vào trang * .... Làm thế nào? bạn chỉ mơ hồ giải thích mã của bạn * nên làm gì, và havent giải thích tất cả những gì vấn đề thực tế là khác mà để nói có một vấn đề. Vui lòng cụ thể hơn – DelightedD0D

+0

Trên kích thước màn hình 768px trở lên, việc nhấp vào một trong ba tiêu đề sẽ hiển thị đoạn nội dung tab tương ứng. Nhấp vào một tiêu đề khác sẽ hiển thị một đoạn nội dung tab khác. Tôi có thể đã không mô tả nó rất tốt nhưng tôi đã cung cấp một Codepen chứng minh vấn đề vì vậy không có lý do để thô lỗ –

+0

Lời xin lỗi của tôi cho đến thô lỗ, tôi chỉ muốn bạn thấy rằng bạn havent cho chúng tôi những thông tin chúng tôi cần để giúp bạn . Chúng tôi rất sẵn lòng trợ giúp, nhưng trước khi chúng tôi đi qua mã trong ví dụ của bạn, chúng tôi cần biết mã * nên làm gì * và * cách mã hiện không thành công * .đây không phải là thường lệ đối với người ngoài vì chúng cho người hỏi. Ví dụ, khi tôi làm cho màn hình nhỏ hơn trong bản demo của bạn, không có gì xảy ra khi tôi nhấp vào tiêu đề, xem http://i.imgur.com/v0JPO2g.png – DelightedD0D

Trả lời

6

Tôi chưa sử dụng jQuery Boilerplate, nhưng tôi tin rằng vấn đề ở đây là với biến số của bạn có tên là plugin.

Không có mã nào trong mã của bạn, bạn khai báo biến có tên là plugin. Khi tôi dừng trình gỡ rối trong Plugin.prototype.showTabContent, tôi có thể đánh giá window.plugin và nó trả về giá trị toàn cầu cho plugin.

Trong hàm tạo cho Plugin, dòng đầu tiên đọc plugin= this;. Vì plugin không được xác định, nó khai báo biến ở phạm vi toàn cục trên đối tượng window.

Khắc phục là chuyển một tham chiếu đến đối tượng plugin khi thiết lập móc $().on(). Dữ liệu được truyền có sẵn trong trình xử lý sự kiện thông qua tham số event được truyền trong thuộc tính data.

Dưới đây là giải pháp (tại 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" 
    }); 
}); 
+0

Tôi đã sử dụng 'plugin = this' để chuyển ngữ cảnh của từ khóa 'this' từ hàm init sang các hàm khác trong plugin như showTabContent. Nếu không, tôi sẽ không thể sử dụng các biến được định nghĩa trong hàm Plugin vì ngữ cảnh của từ khoá 'this' này sẽ thay đổi. –

+0

Tôi thấy rằng, tuy nhiên đây là nguyên nhân gốc rễ của sự thất bại. Bạn đang sử dụng biến toàn cầu và ghi đè giá trị khi khởi tạo phiên bản thứ hai của plugin. Bạn cần phải tìm cách tiếp cận khác. –

+0

Cảm ơn tôi đánh giá cao sự trợ giúp. Bạn có biết một cách khác tôi có thể vượt qua bối cảnh của các trường hợp plugin xung quanh để tôi có thể giữ các biến của tôi tự chứa cho mỗi trường hợp plugin? –

1

Tôi viết lại mã của bạn theo tiêu chuẩn tạo Plugin jQuery.

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

Về cơ bản, tôi đã làm hai điều:

  • chuyển đi từ việc sử dụng dữ liệu thuộc tính cho phong cách (chuyển sang sử dụng một lớp .active thay)
  • chuyển đi từ việc sử dụng this ở khắp mọi nơi, như nó mang lại một làn sóng toàn bộ các vấn đề ràng buộc ...

$.fn.tabs2Accordion l oops qua tất cả các bộ chọn và áp dụng $.tabs2Accordion. Nó cũng trả về bộ chọn cho chuỗi (nó là một tiêu chuẩn trong jQuery).

Sau đó, tất cả các phương thức nội bộ là các biểu thức hàm có cùng phạm vi với tất cả các biến "this cũ" của bạn. Điều này đơn giản hóa mã rất nhiều khi bạn có thể tham khảo các biến đó mà không chuyển chúng vào như một tham số hoặc không cần phải .bind(this) bằng cách nào đó.

Cuối cùng, chức năng cũ init() đã biến mất. Thay vào đó, tôi đặt mã ở cuối hàm $.tabs2Accordion.

Hy vọng điều này sẽ hữu ích!

(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' 
    }) 
}) 
Các vấn đề liên quan