2016-03-13 9 views
8

Sto scrivendo un plug-in JQuery per un progetto su cui sto lavorando per passare dai contenuti a schede sui dispositivi desktop a una fisarmonica su dispositivi mobili. Ho usato JQuery Boilerplate (https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/dist/jquery.boilerplate.js) come schema iniziale per il mio plugin.Il plug-in JQuery non funziona se utilizzato in più posizioni in una singola pagina

Il plugin è chiamato ogni elemento con la classe ".tabs2accordion", come illustrato di seguito:

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

Il plugin funziona come previsto se v'è un solo elemento con classe ".tabs2accordion" su una pagina, ma inizia a malfunzionarsi non appena un altro elemento con la stessa classe viene aggiunto alla pagina. Ho creato una codepen del codice di base per dimostrare il problema. Per mostrare il problema, su una dimensione della finestra di> 768px, prova a fare clic su uno qualsiasi dei titoli e osserva come il contenuto sottostante cambia quando viene fatto clic su ciascun titolo. Quindi rimuovi il blocco dell'HTML e prova a fare nuovamente clic sui titoli.

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

ho cercato loop attraverso ogni elemento con la classe "tabs2accordion" come questo:

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

ma questo non risolvere il problema neanche.

Qualche idea?

+0

* ma inizia a malfunzionarsi non appena un altro elemento con la stessa classe viene aggiunto alla pagina * .... In che modo? hai solo vagamente spiegato che cosa dovrebbe * fare il tuo codice * e non hai affatto spiegato quale sia il vero problema per dire che c'è un problema. Si prega di essere più specifici – DelightedD0D

+0

Nelle dimensioni dello schermo 768px e superiori, facendo clic su uno dei tre titoli verrà visualizzato il paragrafo del contenuto della scheda corrispondente. Facendo clic su un altro titolo verrà visualizzato un altro paragrafo del contenuto della scheda. Potrei non averlo descritto molto bene ma ho fornito un Codepen che dimostrava il problema quindi non c'era motivo di essere scortesi –

+0

Le mie scuse per essere venuta maleducata, voglio solo che tu veda che non ci hai dato le informazioni di cui abbiamo bisogno per aiutarti . Siamo tutti felici di aiutarti, ma prima di passare al codice nel tuo esempio, dobbiamo sapere che cosa deve fare * e * il codice * come il codice attualmente fallisce *. Questi non sono così comuni per un estraneo così come sono al richiedente Ad esempio, quando ridimensiono lo schermo nella tua demo, non succede nulla quando clicco i titoli, vedi http://i.imgur.com/v0JPO2g.png – DelightedD0D

risposta

6

Non ho usato jQuery Boilerplate, ma credo che il problema qui sia con la variabile chiamata plugin.

In nessun punto del codice si dichiara una variabile denominata plugin. Quando interrompo il debugger in Plugin.prototype.showTabContent, posso valutare window.plugin e restituisce il valore globale per il plug-in.

Nel costruttore per Plugin, la prima riga legge plugin= this;. Poiché plugin non è definito, sta dichiarando la variabile nell'ambito globale sull'oggetto window.

La correzione consiste nel passare un riferimento all'oggetto plugin durante l'impostazione del gancio $().on(). I dati passati sono disponibili nei gestori eventi tramite il parametro event passato nella proprietà data.

Ecco la soluzione (a 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

Ho usato 'plugin = this' per passare il contesto della parola chiave 'this' dalla funzione init ad altre funzioni all'interno del plugin come showTabContent. Altrimenti non sarei in grado di utilizzare variabili definite nella funzione Plugin perché il contesto della parola chiave 'this' sarebbe cambiato. –

+0

Vedo che, tuttavia questa è la causa principale dell'errore. Si sta utilizzando una variabile globale e si sovrascrive il valore durante l'inizializzazione della seconda istanza del plug-in. Devi trovare un altro approccio. –

+0

Grazie, apprezzo l'aiuto. Sai un altro modo per passare il contesto dell'istanza del plugin in modo da poter mantenere le mie variabili autosufficienti in ogni istanza del plugin? –

1

ho riscritto il codice seguente norma creazione Plugin jQuery.

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

Fondamentalmente, ho fatto due cose:

  • allontanato da utilizzare i dati attributi per uno styling (commutata ad usare una classe .active invece)
  • allontanato dal usando this ovunque, come porta un'intera ondata di problemi vincolanti ...

$.fn.tabs2Accordion l oops attraverso tutti i selettori e applica $.tabs2Accordion. Restituisce anche il selettore per il concatenamento (è uno standard in jQuery).

Quindi, tutti i metodi interni sono espressioni di funzione che sono nello stesso scope di tutte le vecchie "variabili" this "vecchie". Questo semplifica notevolmente il codice in quanto è possibile fare riferimento a tali variabili senza passarle come parametro o senza doverlo fare in qualche modo con .bind(this).

Infine, la vecchia funzione init() non funziona. Invece, ho inserito il codice alla fine della funzione $.tabs2Accordion.

Spero che questo aiuti!

(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' 
    }) 
}) 
Problemi correlati