2013-05-14 28 views
11

Piszę wtyczkę jQuery, która powinna obsługiwać dodatkowe informacje o łączach, aby określić zachowanie otwarte.Wtyczka jQuery do zastosowania również na dynamicznie tworzonych elementach

Na przykład, chcę obsługuje znaczników takich jak:

  1. <a href="somewhere" data-openmode="NewWindow" class="openmode" />
  2. <a href="somewhere" data-openmode="Modal" class="openmode" />
  3. <a href="somewhere" class="openmode" /> <!-- Not specified -->

Pierwszy powinien otworzyć się w nowym oknie, drugi w oknie modalnym Trzeci powinien otwierać się z natywnym zachowaniem (jakikolwiek cel został ustawiony na tagu).

Chciałbym utworzyć wtyczkę dla tego zachowania tak generycznego, jak to tylko możliwe. Pisałem już:

(function ($) { 
    $.fn.myOpenMode = function() { 
     return this.mousedown(function() { 
      var $this = $(this); 

      var mode = $this.data("openmode"); 
      var href = this.href; 
      var handled = true; 
      if (mode) { 
       switch (mode) { 
        case "NewWindow": 
        case "1": 
         window.open(href, "_blank"); 
         break; 
        case "Dialog": 
        case "2": 
         openAsDialog(href); 
         break; 

        // Actually, there are other options, but I removed them for clarity 

        default: 
         handled = false; 
       } 
      } 
      else { 
       handled = false; 
      } 
      return !handled; 
     }); 
    }; 
})(jQuery); 

Ten kod pozwala mi zadzwonić z dowolnego widoku coś takiego:

$(function(){ 
    $(".openmode").myOpenMode(); 
}); 

ten pracuje dla statycznie wygenerowanych tagów. Jednak moje aplikacje mogą generować znaczniki dynamicznie (przez większość czasu przy użyciu jsRender, ale to nie ma znaczenia).

Ale ponieważ to zachowanie jest skonfigurowane tylko raz, gdy plik javascript jest załadowany, nie pobiera obiektów generowanych dynamicznie.

Co należy zrobić, aby spełnić moje wymagania?

  1. Próbowałem użyć metody on do monitorowania zdarzeń obciążenia, ale to nie działa:

    $(function(){ 
        $(document).on("load",".openmode", function() { $(this).myOpenMode(); }); 
    }); 
    

    Rozumiem to nie działa, ponieważ zdarzenie „obciążenie” nie propaguje

  2. Zastanawiam się nad modyfikacją wtyczki, aby umieścić "na" w wtyczce, ale nie podoba mi się ten pomysł, ponieważ wprowadza pewne zachowanie poza zakresem w wtyczce

  3. Mogę też wywoływać wtyczkę za każdym razem, gdy tworzony jest węzeł dynamiczny, ale także wprowadzać zależności do innych części. Moja wtyczka nie będzie tak autonomiczna, jak bym chciał.

Czy ktoś ma sugestię, aby obsłużyć moje wymagania, utrzymując moją wtyczkę tak odizolowany, jak to możliwe?

[edytuj] Powinno to działa z IE8 i później (a najlepiej z innych przeglądarek)

[edytuj] o to jsFiddle które ilustrują problem (wystarczy kliknąć na Add i spróbuj kliknąć na nowo utworzony element).

+3

.Na() służy do obsługi zdarzeń, a nie wtyczki.Musisz wywoływać wtyczkę za każdym razem, gdy tworzony jest węzeł dynamiczny lub za pomocą obserwatorów, aby sprawdzić, kiedy nowy element (.openmode) zostanie dodany do DOM i zakodować logikę zgodnie z tym. Oczywiście obserwatorzy mogą mieć problem z wydajnością. –

+0

wewnątrz wtyczki, której nie używasz .on(). –

+0

@roasted: Nie jestem fanem używania 'on()' wewnątrz wtyczki. Jednakże, o ile rozumiem moje wyszukiwania google, obserwatorzy nie istnieją z IE i są zabójcami wydajności. –

Odpowiedz

8

Wtyczki dodane do $.fn powinny mieć zastosowanie tylko do wymienionych elementów, a nie żadnych przyszłych elementów.

należy skoncentrować się na posiadające swoje wtyczki zapewniają mechanizm, np

(function($) { 

    $.fn.openmode = function(cmd) { 
     cmd = cmd || 'on'; 

     switch (cmd) { 

      case 'click': 
       // read props, open windows, etc 
       break; 

      case 'on': 
       this.addClass('openmode'); 
       break; 

      case 'off': 
       this.removeClass('openmode'); 
       break; 
     } 
    }); 

})(jQuery); 

a następnie pozwolić użytkownikowi wtyczki zarejestrować procedurę obsługi zdarzenia, które wyzwalacze że mechanizm, za pomocą delegacji zdarzeń w razie potrzeby:

$(document).on('click', 'a.openmode', function() { 
    $(this).openmode('click'); 
}); 

drugi kod może być wprowadzony do nazw jQuery także jako funkcja jej przydatność

(function($) { 
    $.openmode = function(cmd) { 
     cmd = cmd || 'on'; 

     switch (cmd) { 

      case 'on': 
       $(document).on('click.openmode', 'a.openmode', function() { 
        $(this).openmode('click'); 
       }); 
       break; 

      case 'off': 
       $(document).off('click.openmode', 'a.openmode'); 
       break; 
     } 
    }; 
})(jQuery); 

taka, że ​​właśnie dzwoni:

$.openmode(); 

zrobi całą pracę wymaganą włączyć wtyczkę dla wszystkich obecnych (i przyszłych) .openmode elementem.

+0

ciekawym podejściem. Spróbuję. –

+0

To jest dobre rozwiązanie IMO –

+0

Maye literówka w kodzie (dwa "case" na ":" klauzula) –

0

Możesz użyć tak zwanego podejścia Obserwatora Mutacji, aby reagować na zmiany w twoim DOM.Oto więcej informacji o nim:

MutationObserver

A oto kilka dobrych przykładów:

DOM manipulations

+0

Nie określiłem tego początkowo, ale muszę wybrać IE8 i później. zgodnie z podanym linkiem nie jest to (jeszcze) obsługiwane. –

1

znalazłem solution, które opierają się na metodzie on(), wewnątrz wtyczki.

Wtyczka jest w tym przypadku oczekuje się selector argument nadal zachować wybierak na poziomie aplikacji (nie na poziomie wtyczki):

(function ($) { 
    $.fn.myOpenMode = function (selector) { 
     return $(document).on("mousedown", selector ,function() { 
      var $this = $(this); 

      var mode = $this.data("openmode"); 
      var href = this.href; 
      var handled = true; 
      if (mode) { 
       switch (mode) { 
        case "NewWindow": 
         alert("NewWindow"); 
         break; 
        case "Dialog": 
         alert("Dialog"); 
         break; 
        default: 
         handled = false; 
       } 
      } else { 
       handled = false; 
      } 
      return !handled; 
     }); 
    }; 
})(jQuery); 

Następnie muszę zmienić wezwanie do mojego pluginu jak ta :

$(function(){ 
    $.fn.myOpenMode(".openmode"); 
}); 

Ten jest działa zgodnie z oczekiwaniami. Jednak nie jestem bardzo pewien przestrzegania wytycznych wtyczki jQuery.

Więc jeśli ktoś ma lepsze rozwiązanie, będę szczęśliwy, aby wiedzieć.

+0

nie powinieneś nigdy wywoływać funkcji '$ .fn' bezpośrednio w ten sposób. Funkcje '$ .fn' są właściwościami kolekcji jQuery, a nie samodzielnymi funkcjami (które powinny być w' $ ') – Alnitak

1

Można nadal realizować swoją własną metodę obserwatora, tutaj rozszerzenie dołączyć metodę:

;(function ($) { 
    var fctsToObserve = { 
     append: [$.fn.append, 'self'] 
    }, fctsObserveKeys = ''; 
    $.each(fctsToObserve, function (key, element) { 
     fctsObserveKeys += "hasChanged." + key + " "; 
    }); 
    var oOn = $.fn.on; 
    $.fn.on = function() { 
     if (arguments[0].indexOf('hasChanged') != -1) arguments[0] += " " + fctsObserveKeys; 
     return oOn.apply(this, arguments); 
    }; 
    $.fn.hasChanged = function (types, data, fn) { 
     return this.on(fctsObserveKeys, types, null, data, fn); 
    }; 
    $.extend($, { 
     observeMethods: function (namespace) { 
      var namespace = namespace ? "." + namespace : ""; 
      var _len = $.fn.length; 
      delete $.fn.length; 
      $.each(fctsToObserve, function (key) { 
       var _pre = this; 
       $.fn[key] = function() { 
        var target = _pre[1] === 'self' ? this : this.parent(), 
         ret = _pre[0].apply(this, arguments); 
        target.trigger("hasChanged." + key + namespace, arguments); 
        return ret; 
       }; 
      }); 
      $.fn.length = _len; 
     } 
    }); 
    $.observeMethods() 
})(jQuery); 

Should działa w starych przeglądarkach też, ale ya obserwatorzy dostał jakiś problem z wydajnością, nawet można je zoptymalizować.

SEE DEMO

+0

IMHO nie ma potrzeby stosowania metody obserwatora. Wtyczka OP robi tylko cokolwiek _użyteczne_ w odpowiedzi na zdarzenie użytkownika, a jQuery ma już doskonale dobry mechanizm obsługi zdarzeń na nowo dodanych węzłach DOM. – Alnitak

+0

@Alnitak Im całkowicie zgadzam się –

1

jestem późno na odpowiedź, ale mam nadzieję, że ktoś pomoże. Miałem ten sam problem.

$.fn.plugin = function(){ 
    //some code 
} 
$(".element").plugin();   //works fine 
$(".elementParent").append(".newElement"); 
$(".newElement").plugin();   // doesn´t work 

To nie działa, ponieważ trzeba jeszcze raz zadzwonić do wtyczki po dodaniu elementu, jak to

function runPlugin(){ 
    $.fn.plugin = function(){ 
     //some code 
    } 
} 
runPlugin();      //call plugin here 
$(".element").plugin();   //works fine 
$(".elementParent").append(".newElement"); 

runPlugin();      // call plugin here 
$(".newElement").plugin();  // works fine 
+0

Znalazłem ten problem. Z Twojego kodu oznacza to, że dodałeś nową funkcję "runPlugin" do pliku JS wtyczki? Próbowałem go, a następnie, gdy wzywam runPlugin(); tak jak to zrobiłeś, pojawia się błąd - runPlugin nie jest zdefiniowany. –

+0

Zgadza się. Pomyśl o wrapperie funkcji Asi jako zmiennej. Za każdym razem, gdy go wywołasz, wywołujesz cały skrypt, który zawiera. W odniesieniu do twojego problemu, nie jestem naprawdę pewien, czy to działa, gdy używasz zewnętrznego pliku ja dla wtyczki. Możesz jednak spróbować a) skopiować plik do swojego dokumentu lub b) dodać $ (dokument) .append ("") przed wywołaniem wtyczki – edvilme

+0

Jednak, gdybym był do wyboru, wybrałbym pierwszą opcję, ponieważ jest bardziej wydajna i nie wymaga ładowania zewnętrznych plików, dzięki czemu skrypt jest szybszy i bardziej niezawodny. – edvilme

Powiązane problemy