Livequery

Posted by Piotr Sarnacki Mon, 03 Mar 2008 20:41:00 GMT

Pisałem jakiś czas temu o nieinwazyjnym javascripcie w ruby on rails. Zapomniałem wtedy napisać o pluginie do jQuery, który ułatwia pracę stosując nieinwazyjny javascript.

Livequery ułatwia znacznie korzystanie ze zdarzeń. Często podczas manipulowania DOM tworzy się i/lub usuwa nowe elementy. Co gdy przypiszemy do kilku elementów jakieś zdarzenie po czym dodamy nowe elementy?

Zmontowałem prosty przykład, na którym można to sprawdzić. Do pierwszej listy linki dodawane są bez żadnych dodatków. Zwykłe:


  $('<li><a href="#">Link</a></li>').appendTo($('.without ul'));

Dlatego funkcja clicked uruchomi się tylko dla 2 pierwszych linków.

W drugim przypadku dodałem bind dla nowo stworzonego elementu:

  $('<li><a href="#">Link</a></li>').
          appendTo($('.with ul')).
          bind('click', clicked);

Zdarzenie jest więc przypisywane również dla nowych linków.

Jednak gdy ilość różnych zdarzeń zaczyna się niepokojąco powiększać, a cały interfejs strony zmienia się przy każdym prawie kliknięciu często można zapomnieć o przypisaniu zdarzeń. Nawet jeżeli ktoś nie jest zapominalski, to zapewne jest leniwy (ponoć dobrego programistę cechuje lenistwo). Na ratunek przychodzi Livequery, plugin, który sam dba o przypisywanie lub usuwanie zdarzeń w razie manipulacji DOM.

Drugi przykład# wykorzystuje już LiveQuery – jak można łatwo zauważyć nie trzeba już dopisywać żadnych dodatkowych funkcji.

I jedno zmartwienie przy pisaniu mniej ;-)

Posted in  | Tags , , ,  | no comments | no trackbacks

Rails 2.0 i js.erb

Posted by Piotr Sarnacki Tue, 15 Jan 2008 20:36:00 GMT

Niedawno pisałem o nieinwazyjnym kodzie javascript w railsach. Wspomniałem tam o pluginie MinusMor, który dodaje pliki ejs. Czyli kod javascript parsowany erbem. Zainstalowałem plugin w mojej aplikacji przeniesionej na rails 2.0. Kod pluginu nie działa… zacząłem przeglądać kod railsów, żeby poprawić minusmor (w internecie nie mogłem znaleźć poprawki). Po chwili przemyślenia sprawdziłem co dzieje się gdy wyrzucę plik rjs. Komunikat o błędzie:


Missing template pages/index.js.erb in view path

Git! W railsach 2.0 możemy przecież wskazać jakiego parsera mają użyć.

Tworzymy plik index.js.erb. I jeszcze mała zmiana, ustawiamy brak layoutu, żeby renderował się sam template:

respond_to do |format|
  render.html
  render.js { render :layout => false }
end

I to wszystko! Po prostu działa.

Nawet lepiej. Można zdefiniować layout, na przykład:


render.js { render :layout => 'jquery' }
a w nim można na przykład napisać:

(function ($) {
  <%= yield %>
})(jQuery);

I można używać w template’ach js.erb $ pomimo tego, że wcześniej użyło się jQuery.noConflict.

Przeszukałem później jeszcze net w poszukiwaniu informacji na ten temat i znalazłem artykuł na mad.ly o dodaniu helperów z MinusMor

Posted in  | Tags , ,  | no comments | no trackbacks

Nieinwazyjny javascript razem z Ruby on Rails

Posted by Piotr Sarnacki Tue, 25 Dec 2007 20:15:00 GMT

Jakiś czas temu Riddle napisał o tym dlaczego nie można zakładać, że ktoś ma włączony javascript. Temat był już wcześniej wiele razy poruszany, mi bardzo podobał się artykuł Chrisa Heilmanna The seven rules of Unobtrusive JavaScript. Zachęcam do zapoznania się z tymi dwoma tekstami – pomogą zrozumieć dlaczego “tracić” czas na rozwiązywanie problemów, o których napiszę poniżej. Streszczając krótko artykuły mogę napisać, że jeżeli to możliwe, należy pisać aplikację tak, żeby działała bez włączonego javascriptu. Skrypty mogą nie działać z kilku powodów:

  • użytkownik ma wyłączony javascript w przeglądarce
  • wystąpi błąd w kodzie (nie możemy sprawdzić kodu na wszystkich urządzeniach)
  • strona nie załaduje się do końca
  • boty wyszukiwarek nie używają javascriptu, więc w skrajnym wypadku możemy uniemożliwić zindeksowanie naszej strony

Jak to wygląda w Railsach?

Railsy są wyposażone w zestaw helperów generujących różnego rodzaju kawałki kodu javascript. Pomysł jest z pozoru bardzo fajny. Początkujący mogą szybko zacząć używać javascriptu razem z dobrodziejstwami, które daje nam Ajax bez znajomości samego języka. Jest jednak sporo minusów używania helperów:

  • generują one dodatkowy niepotrzebny kod javascript. jeżeli na stronie mamy kilkadziesiąt linków, a każdy z nich ma wklejony kod `onclick=”new Ajax.Request(’/controller/action?n=33’, {asynchronous:true, evalScripts:true, onComplete:function(request){undoRequestCompleted(request)}}); return false;”` strona będzie ważyć dużo więcej.
  • javascript w nich użyty jest “inwazyjny”. Jeżeli javascript nie będzie działał, to element wygenerowany w taki sposób również nie zadziała1
  • ciężko jest się przy nich trzymać zasady DRY. Mając 5 linków wygenerowanych metodą link_to_remote z takimi samymi opcjami, za każdym razem gdy coś musimy zmienić, zmieniamy to w 5 miejscach. Powinno się oczywiście napisać helpera, który wygeneruje link z danymi opcjami. Tylko chyba nie tędy droga – w dalszej części artykułu postaram się pokazać dlaczego nieinwazyjny javascript jest lepszy w tego typu zadaniach.
  • z mojego doświadczenia wynika, że bardzo często, gdy ilość kodu się powiększa i javascript generowany przez railsy miesza się z tym z plików js można łatwo się pogubić. Tyczy się to także różnego rodzaju api – na przykład google maps. Najłatwiej działać, gdy javascript jest odseparowany od kodu railsów
  • jesteśmy związani z jedną biblioteką (w tym wypadku prototype+script.aculo.us) – jeżeli chcemy zamienić ją na coś innego (ja ostatnio przesiadłem się na jQuery, zastanawiałem się też nad YUI) helpery przestaną działać – można je oczywiście przepisać, ale komu by się chciało. DHH nie zamierza nic w tej kwestii zmieniać, więc na zmianę tego w Railsach nie ma co czekać.

Jakie są minusy? Trzeba lepiej poznać javascript (właściwie dla mnie to nie jest minus, ale dla niektórych być może tak). Nie jest to jednak przeszkoda nie do pokonania dla początkujących. Javascript, który jest potrzebny do zadań możliwych do wykonania z użyciem samych helperów nie jest z reguły przesadnie trudny do nauczenia. Ratunkiem dla osób, które nadal chcą korzystać z helperów jest plugin UJS. Jeżeli bardzo nie chcesz pisać wszystkiego w czystym javascripcie, to jest to bardzo fajne połączenie prostoty helperów i zalet nieinwazyjnego javascriptu. Jest ona jednak pisana dla Prototype’a, więc tak jak w ostatnim punkcie z powyższej listy można o niej zapomnieć, jeżeli używana jest jakakolwiek inna biblioteka.

W komentarzach apohllo zauważył, że plugin UJS nie jest już rozwijany. Używacie na własną odpowiedzialność. :)

Przejdę do przykładów, bo przecież nie samą teorią człowiek żyje.

Przerzuciłem się ostatnio na jQuery i chyba przy niej zostanę. Rozumiem jednak, że większość użytkowników railsów jest związana z Prototype’em, więc kod będę podawał w dwóch wersjach, dla Prototype’a i jQuery.

Na początek wprowadzenie. Co zrobić, żeby wyrzucić z htmla (i railsów) wstawki Javascript? Wszystko wstawiamy do aplikacji używając zdarzeń. Przypuśćmy, że mamy linka o id=”someLink”. Zamiast dopisania onclick:


    <a href="#" onclick="alert('Klik!'); return false;">Link</a>
  
należy użyć:

Prototype:

    Event.observe($('someLink'), 'click', function(event) {
      alert('Klik!');
      Event.stop(event);
    }
  
jQuery:

    $('#someLink').click(function (){
      alert('Klik!');
      return false;
    });
  

Oba przykłady dodają zdarzenie uaktywniane kliknięciem w linka. Ostatnia linijka w obu funkcjach, które są wykonywane po kliknięciu (nazywane są z reguły handlerami) jest wstawiona po to, żeby kliknięcie linka nie przeładowało strony.

Kod taki w aplikacji Rails można wrzucić do pliku application.js, lub jakiegoś specyficznego pliku js ładowanego na danej stronie. Należy też go załadować dopiero po wczytaniu się całego dokumentu. Normalnie coś takiego uzyskiwało się wpisując w body: `onload=”jakasFunkcjaJavascript();”`, ale takie dodawanie jest passe, więc:

Prototype:

    Event.observe(window, 'load', function() {
      //kod który wykona się po załadowaniu strony
    }

    // lub zdefiniowana wcześniej funkcja, która wykona się po załadowaniu strony
    Event.observe(window, 'load', jakasFunkcjaJavascript());
  
jQuery:

    $(function() {
      //kod który wykona się po załadowaniu strony
    });

    // lub zdefiniowana wcześniej funkcja, która wykona się po załadowaniu strony
    $(jakasFunkcjaJavascript);
    //powyższe przykłady, to skrócone wersje document.ready:
    $(document).ready(function () {});
  

Dzięki tym konstrukcjom mamy pewność, że kod wykona się dopiero gdy załaduje się cały dokument, a nie w momencie, gdy dołączony jest plik js.

Teraz przykład prostego zapytania ajax (przykład z dokumentacji railsów):

  link_to_remote 'hello', :url => { :action => "action" }, 
    404 => "alert('Not found...? Wrong URL...?')",
    :failure => "alert('HTTP Error ' + request.status + '!')" 
  # Wygeneruje: <a href="#" onclick="new Ajax.Request('/testing/action', {asynchronous:true, evalScripts:true,
  #            on404:function(request){alert('Not found...? Wrong URL...?')},
  #            onFailure:function(request){alert('HTTP Error ' + request.status + '!')}}); return false;">hello</a>
Jak widać powyżej wygenerowanego kodu jest całkiem sporo. Jeżeli będzie trzeba wstawić taki link w paru miejscach dobrze by było napisać swojego własnego helpera, który automatycznie będzie wklejał komunikaty o błędach. Jak można to zrobić lepiej? Na początek wystarczy stworzyć zwykłego linka z jakąś klasą, lub id:

  link_to 'hello', { :action => 'action' }, :class => 'ajax'
Teraz trzeba użyć trochę javascriptu :

        $$('a.ajax').each(function (element) {
          Event.observe(element, 'click', function(event) {
            new Ajax.Request(this.readAttribute('href'), {asynchronous:true, evalScripts:true, 
              on404:function(request){alert('Not found...? Wrong URL...?')}, 
              onFailure:function(request){alert('HTTP Error ' + request.status + '!')}}); 
            Event.stop(event);
          });
        });

Na początku pobieramy wszystkie linki z klasą ajax i dla każdego z nich wywołujemy funkcję `Event.observe(element, ‘click’....`. Dalszy kod wykona się więc po kliknięciu w danego linka. W tym wypadku wykonujemy zapytanie ajaxowe (`new Ajax.Request`). Pierwszy argument to atrybut href linka (uwaga, kod ten nie zadziała w starszych wersjach prototype’a, które niepoprawnie obsługiwały this w tego typu funkcji). Reszta kodu to standardowe opcje, po więcej odsyłam do dokumentacji Prototype.

A w jQuery wyglądać to będzie tak:

    $('a.ajax').click(function (){
      $.ajax({
        url: this.href,
        dataType: "script",
        beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");},
        error: function(){
          alert( "Error loading page");
        }
      });     
      return false;
    });

Kod zasadniczo robi to samo, co poprzedni przykład. Można przy okazji porównać prostotę jQuery i porównać ją z Prototype’em (ostatnio dużo się w tej bibliotece pozmieniało, a ja nie jestem na bieżąco, więc jeżeli ktoś zna lepszy sposób na napisanie czegoś takiego, to proszę o komentarz). Skomentuję tylko atrybuty dataType i beforeSend w funkcji ajax(). Ustawiając je w taki sposób przekazujemy serwerowi, że chcemy dostać odpowiedź jako skrypt i akceptujemy typ MIME “text/javascript”. Należy te 2 rzeczy dodać, ponieważ inaczej nie będzie renderować się plik RJS. Więcej na ten temat w artykule jQuery Ajax + Rails

Rozwiązanie proste i efektywne. Żeby link wykonał javascript wystarczy dodać do niego klasę ajax. Jeżeli strona i kody javascript nie wczytają się, link dalej będzie działał poprawnie. Przy założeniu, że poprawnie obsłużymy wszystko w kontrolerze. Służy do tego metoda `respond_to`


  respond_to do |format|
    format.js # jeżeli to zapytanie wykonane ajaxem uruchomi się plik RJS
    format.html # w przeciwnym wypadku wyrenderowany zostanie template rhtml
  end

Więcej o takim sposobie renderowania templatów pisał na przykład Jamis Buck.

Można też w podobny sposób zamienić zwykłą formę na taką wysyłaną ajaxem:

Prototype:

       $$('form.ajax').each(function (element) {
          Event.observe(element, 'submit', function(event) {
            new Ajax.Request(this.readAttribute('action'), {
              parameters: Form.serialize(this),
              asynchronous:true, 
              evalScripts:true
              }); 
            Event.stop(event);
          });
        });  
  

Powyższy kod jest bardzo podobny do poprzedniego przykładu. Różnica polega na tym, że zdarzeniem nie jest ‘click’ tylko ‘submit’ i jako parametry podajmy wynik funkcji `Form.serialize(this)` – zbiera ona wartości pól i zwraca string typu: “pole1=wartosc1&pole2=wartosc2”

jQuery:

    $("form.ajax").ajaxForm({
      dataType: 'script',
      beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");},
      resetForm: true
    });
  

W jQuery najłatwiej skorzystać z pluginu jQuery Form – załatwia on za nas wszelkie formalności ;-)

W ten sposób zmiana jakiegoś linka lub formy na jego ajaxową formę to kwestia dodania jednej klasy. Można oczywiście napisac wiele takich funkcji dla różnych przypadków, dowiązanych do tagów z innymi klasami, lub z konkretnym id.

Na koniec krótkie podsumowanie.

Kod javascript dodajemy do aplikacji tak, żeby nie zablokować dostępu w wypadku braku jego wykonania. Zapomnieć można o wszelakich “onclick” i innych tego typu sprawach. Wszystko powinno być dołączone jako zdarzenia. Dzięki temu zmniejsza i upraszcza się kod railsów i ten przez nie generowany.

Warto obejrzeć również plugin MinusMOR, który zmienia trochę podejście do javascriptu. Zamiast plików rjs, w których używamy rubiego zamienianego później na javascript, mamy pliki ejs, w których wpisujemy kod javascript z możliwością wstawiania kodu rubiego. Tak samo jak w rhtmlu poprzez <% %>.

W komentarzach apohllo zauważył, że plugin MinusMOR nie jest już rozwijany. Trzeba o tym pamiętać zaczynając go używać. Z drugiej strony widziałem kod pluginu i rejestruje on tylko nowe rozszerzenie “ejs”. Na początku szukane będą pliki z tym rozszerzeniem, a jeżeli ich nie będzie Railsy wyrenderują RJS. W każdym razie zaczynacie używać na własną odpowiedzialność. :)

Zapraszam do komentowania – prosiłbym o opinie dotyczące tego typu artykułów. Czy są zrozumiałe? Czy przydają się wam? Konstruktywna krytyka mile widziana. :)


  1. W tym miejscu trzeba zaznaczyć, że jest możliwość wygenerowania linku, czy formy, która będzie działała przy wyłączonym javascripcie, ale niewiele osób o tym wie i z tego korzysta. I trzeba dopisać 2 url, który z reguły jest taki sam – łamana jest zasada DRY

Posted in  | Tags , , ,  | 3 comments | 1 trackback

jQuery.noConflict i $ - fajny trick

Posted by Piotr Sarnacki Tue, 18 Sep 2007 11:46:00 GMT

Dzisiaj króciutko: fajny trick podpatrzony gdzieś w skryptach jQuery.

Pisząc skrypty najlepiej robić to tak, żeby można było je uruchamiać na każdej stronie. Nie mogą się gryźć z innymi bibliotekami. Dlatego jQuery wprowadziło funkcję:

jQuery.noConflict();

Po jej wywołaniu kontrola $ jest zwracana bibliotece, która wcześniej go używała. Dzięki temu można używać jQuery w połączeniu z innymi bibliotekami, a nawet ze starszymi wersjami jQuery (bardzo przydatne przy pisaniu widgetów). Minusem jest konieczność pisania jQuery zamiast $. Można oczywiście przypisać jQuery na krótszą zmienną, ale wtedy ciężej jest wklejać kod napisany przez innych.

Aby temu zaradzić wystarczy użyć takiej konstrukcji:


(function($) {
  //tutaj można używać $ bez strachu o konflikt
})(jQuery);

Stworzona zostaje funkcja, która jest od razu wywołana z podanym argumentem. I voilla :)

Posted in  | Tags , ,  | 4 comments | no trackbacks

Nowy jQuery UI

Posted by Piotr Sarnacki Mon, 17 Sep 2007 09:08:00 GMT

Tak jak zapowiedziano już wcześniej, wypuszczona została wersja 1.0 jQuery User Interface.

Uruchomiona została nowa, dużo ładniejsza strona, dodane zostały nowe przykłady i dema.

Celem twórców biblioteki jest stworzenie zwartego zestawu komponentów, dobrze napisanych i udokumentowanych. Bardzo dobrze, że takie projekty powstają, bo wrzucanie 15 pluginów z różnych źródeł, co do przyszłości których nikt nie ma pewności jest gorszym rozwiązaniem. Na stronie jest dostępny downloader, w którym zaznaczamy potrzebne elementy, po czym tworzony jest plik zawierający spakowane kommponenty.

Naprawdę polecam. :)

Tags , , , ,  | 1 comment | 4 trackbacks

Generowanie HTMLa w javacripcie

Posted by Piotr Sarnacki Sun, 16 Sep 2007 10:43:00 GMT

Czasami bywa tak, że jakaś wyższa siła każe nam generować duże ilości HTMLa. Wygląda to dość nieładnie:


var el = document.createElement("div");
el.setAttribute('id','elementId');
el.setAttribute('class','someClass');
document.body.appendChild(el);

Stworzony został jeden element. A jakby elementów było 10? I każdy miałby po parę atrybutów? Masakra.

Na szczęście powstają różnego rodzaju DOM buildery. Od wersji 1.5.1 użytkownicy prototype’a będą mogli cieszyć się jego dobrodziejstwami. Jeżeli chodzi o jQuery to znalazłem dość fajną implementację. W komentarzach jest też inna wersja, która mi osobiście bardziej odpowiada.


$.create(
   'table', { 'class':"MyTable" }, [
      'tbody', {}, [
         'tr', {}, [
            'td', { 'class':"MyTableCol1" }, [ "howdy" ],
            'td', { 'class':"MyTableCol2" }, [
               "Link:", 'a', { 'class':"MyLink", 'href':"http://www.example.com" }, ["example.com"] ] ] ] ]
).appendTo(document.body);

Całkiem ładnie to wygląda. używając createElement wyszłoby pewnie ze 20 linijek.

Są oczywiście inne implementacje, które nie wymagają jQuery czy prototype’a, przydatne szczególnie w przypadku różnego rodzaju widgetów. Odsyłam do wujka G ;)

Tak jak obiecałem, wrzucam ostateczne wersje zaproponowanych w powyższym linku funkcji. Teraz powinno działać bez zarzutu.


jQuery.create = function() {
  var ret = [], a = arguments, i, e;
  a = a[0].constructor == Array ? a[0] : a;
  for(i=0; i<a.length; i++) {
    if(a[i+1] && a[i+1].constructor == Object) { // item is element if attributes follow
      var tag = a[i];
      attrs = a[++i];
      if(jQuery.browser.msie && (attrs.name || attrs.checked)) {
        tag = '<' + tag + ' ' + (attrs.name ? 'name="' + attrs.name + '" ' : '')+ (attrs.checked ? "checked" : " ") +'>';
        if(attrs.name) delete attrs.name;
        if(attrs.checked) delete attrs.checked;
      }
      e = document.createElement(tag);
      jQuery(e).attr(attrs); // apply attributes
      if(a[i+1] && a[i+1].constructor == Array) jQuery(e).append(jQuery.create(a[++i])); // optional children
      ret.push(e);
    } else { // item is just a text node
      ret.push(document.createTextNode(a[i]));
    }
  }
  return ret;
};

jQuery.tpl = function(json, tpl) {
  var ret = [];
  jQuery.each(json.constructor == Array ? json : [json], function() {
    var o = jQuery.create(tpl.apply(this));
    for(var i=0;i<o.length;i++) ret.push(o[i]);
  });
  return ret;
};

Posted in  | Tags , , , ,  | 3 comments | 1 trackback

jQuery vs Prototype

Posted by Piotr Sarnacki Tue, 04 Sep 2007 12:10:00 GMT

Kilka dni temu wrzuciłem notkę promującą jQuery. Często podczas porównywania narzędzi przeżywam pewien rodzaj euforii, która nie pozwala mi na obiektywne rozpatrzenie wszystkich za i przeciw. Przez chwilę wydaje mi się, że znalazłem narzędzie idealne (co oczywiście jest okropnym nadużyciem. jak ogólnie wiadomo idealny jest tylko polski rząd i jego sukcesy na przestrzeni ostatnich 2 lat – widziałem w reklamach).

W każdym razie ochłonąłem trochę i ze spokojem buddyjskiego mnicha (mówiłem już, że jestem oazą spokoju?) przetrząsnąłem cały internet w poszukiwaniu informacji. Próbowałem nawet wywołać flamewara na google groups. I biednie jest. Niewiele znalazłem. Wszędzie jakieś artykuły, których sens jest mniej więcej taki: “Przez długi czas używałem prototype(jQuery) i stwierdziłem, że muszę dać jQuery(prototype) szansę. jQuery jest prototype killerem!”. Nie licząc fajnego artykułu na agile ajax. Stety albo niestety autor nie do końca chce wyjawić, które podejście jest dla niego lepsze. Aczkolwiek pewną wskazówką może być przesiadka na jQuery, a nie odwrotnie.

No i krytyka modelu dziedziczenia zastosowanego w prototype.

Pozwoliłem sobie zrobić własne porównanie. Mam nadzieję, że chociaż w małym procencie obiektywne. ;-)

Manipulacja DOM i CSS

Na tym polu zdecydowanie wygrywa jQuery. Twórca zapewnił coś, co nazywa się “chainability”. Metody manipulujące elementami HTMLa można wywoływać w łańcuchach, na przykład jQuery('#some').show('slow').css('background-color', 'white').add('<p>Dodatkowy paragraf!</p>').

Jeżeli chodzi o same selektory, to od wersji 1.5.0 prototype ma obsługę selektorów CSS, więc nie ma już problemu z wybieraniem elementów w bardzo fikuśnych konfiguracjach ;-)

jQuery ma jeszcze jedną ciekawą właściwość. Wybrane elementy zawsze zwracane są w tablicy dzięki czemu biblioteka sama zadba o przeiterowanie po kolekcji i zaaplikowaniu metody do każdego jej elementu. Na przykład zamiast napisać (prototype):


$$('a').each(function(v, i) { Event.observe(v, 'click', fn); });
Możemy użyć:

jQuery('a').click(fn);

Wydajność

Na tym polu prototype mocno dawał w kość jQuery, ale sytuacja się poprawiła. W poprzednim poście zamieściłem 2 screeny, na których można zauważyć, że obecnie jQuery jest jakieś 2 razy wolniejsza (oczywiście jeżeli chodzi o same selektory). Dla innych metod podejrzewam, że będzie podobnie (dla each i map na pewno – jeżeli ktoś się bardzo nudzi może się pobawić i zrobić więcej benchmarków).

Ale statystyki na szczęście nie zawsze oddają to, co dzieje się w rzeczywistości. Jeżeli ktoś dysponuje odpowiednio wolnym komputerem (kilkaset mhz najlepiej), łatwo może sprawdzić, że efekty napisane w script.aculo.us bardziej “mulą” kompa niż ich odpowiedniki w jQuery (linux, firefox2 – wydaje mi się, że na innych systemach i przeglądarkach będzie podobnie). Szczególnie widać tą różnicę przy wszelakich tworach drag&drop.

Rozszerzenia

W tym miejscu wystarczy spojrzeć na listę rozszerzeń dla jQuery. Tego chyba nic w tej chwili nie pobije. Rozszerzenia z zakresu UI dla prototypa (a właściwie script.aculo.us) wyglądają biednie w takim porównaniu.

Duży plus dla jQuery.

Społeczność

jQuery jest niewątpliwie biblioteką popularniejszą. Niektórzy mówią, że to chwilowy buzz (znowu ulegam modzie frameworkowej? :).

Prototype z kolei, chociaż mniej popularny, ma mocne wsparcie w społeczności RoR i wsparcie Rails Core Team. Podobno niektórzy pracują nad czymś co można nazwać “jQuery on Rails”, ale nadal prototype jako jedyny jest dostępny “out of the box”.

Rozszerzenia języka.

Prototype dodaje do javascriptu bardzo dużo metod, wzorowanych na metodach rubiego, ułatwiających pracę z wbudowanymi typami. Między innymi moduł Enumerable, Hash, sporo metod do String, Date i dużo dużo więcej.

jQuery z kolei udostępnia tylko kilka metod (each, map, trim, grep…). Oczywiście można dodać swoje metody, ale w tym miejscu trzeba wspomnieć o filozofii jQuery.

Prototype dodaje nowe metody do poszczególnych klas, dzięki czemu można napisać na przykład:


['a', 'b', 'c'].each(function(v, i) { alert(v); });

W jQuery wyglądałoby to w ten sposób:


jQuery.each(['a', 'b', 'c'], function(v, i) { alert(v); });

Jeszcze nie jestem do końca pewien, które podejście mi bardziej pasuje. Być może połączenie jQuery z podejściem prototype?


jQuery.extend(Array.prototype, {
  each: function(iterator) {
    jQuery.each(this, iterator);
  }
});

I już można użyć konstrukcji analogicznej do prototype. Po paru zmianach można dodać też resztę funkcji z Enumerable – po odpowiednim przygotowaniu potrzeba niewiele więcej niż copy&paste. Podobnie łatwo będzie z resztą rozszerzeń.

Można również zostać przy podejściu jQuery i napisać metody działające mniej więcej tak:


jQuery.collect(arr, function(v, i) { /* jakiś kod */});

Chwilę po dodaniu tego posta przypadkiem trafiłem na fajny artykuł, który pokazuje dlaczego podejście a’la prototype jest po ciemnej stronie mocy ;-)

W następnym poście opiszę jak można to szybko, łatwo i przyjemnie zrobić. (o ile starczy mi czasu i samozaparcia)

Od wersji 1.6.0 prototype dostanie też parę bardzo przydatnych funkcji i narzędzi. Między innymi DOMBuilder, custom events, wrap() – bardzo podobają mi się takie smaczki. Więcej informacji w notce na stronie prototype

Podsumowując ten punkt, jeżeli chcemy intensywnie korzystać z wymienionych powyżej metod (polecam przejrzeć dokumentację prototype) lepszym wyborem będzie prototype. Z zaznaczeniem, że sporo z tych funkcji można bardzo łatwo przenieść do jQuery ;-)

Materiały

Przez pewien czas, dawno dawno temu, prototype nie miał prawie dokumentacji, co mogło niektórych zniechęcać. Aktualnie można powiedzieć, że zarówno prototype, jak i script.aculo.us są dobrze udokumentowane i raczej nikt nie będzie miał problemu z nauką. Tak samo zresztą jest z jQuery.

Oba framweorki doczakały się książek.

Podsumowanie

Ciężko mi wybrać ten lepsiejszy framework. I na pewno wybór ten nie będzie obiektywny – każdy musi sam rozważyć czego oczekuje po bibliotece.

Wydaje mi się, że przy projekcie, który w tym momencie zaczynam bardziej sprawdzi się jQuery. Mam w głowie ułożony zaawansowany interfejs użytkownika i nie chciałbym żadnych zgrzytów, a jak pokazuje przykład scriptaculous, płynność różnych efektów nie ma wiele wspólnego z szybkością biblioteki.

Z drugiej strony jeżeli ktoś uważa, że będzie wykonywał po stronie użytkownika sporo obliczeń, pętli, działania na dużych tablicach itp. itd. prawdopodobnie dobrym wyborem będzie prototype – nie dość, że jest łatwiej wykonywać te czynności, to jeszcze wszystko działa szybciej.

Mam nadzieję, że powyższy opis pomoże w wybraniu biblioteki lepszej do rozwiązania danego problemu. Jeżeli pominąłem jakieś soczyste właściwości którejś z bibliotek proszę o komentarz – uzupełnię porównanie :)

Posted in  | Tags , , , , ,  | 3 comments | no trackbacks

Dlaczego jQuery?

Posted by Piotr Sarnacki Fri, 31 Aug 2007 18:17:00 GMT

Jako javascriptowego frameworka (z góry przepraszam ukrytych purystów językowych za brutalne wrzucanie spolszczonych angielszczyzmów – w razie czego proszę o wersję przetłumaczoną w komentarzu, trochę humoru zawsze się przyda) przez długi czas używałem zestawu prototype + script.aculo.us. Po krótkim czasie przywiązałem się i stwierdziłem, że nie ma sensu sprawdzać innych możliwości
  • bo i tak większość frameworków oferuje podobne możliwości
  • będę musiał stracić czas na nauczenie się czegoś nowego
  • Ruby on Rails wspiera powyżej wymieniony zestaw

Właściwie to “wybranie” prototype’a było bezpośrednim następstwem ostatniego punktu. Pomyślałem, że skoro DHH wybrał ten framework, to coś w tym musi być. Podejrzewam, że w rzeczywistości było to spowodowane tym, że jQuery miał w tamtym okresie małą popularność (o ile w ogóle istniał). Patrząc na to jak wyglądają obie biblioteki można odnieść wrażenie, że filozofię dużo bliższą Railsom realizuje jQuery.

Najlepiej to widać patrząc na przykłady:

prototype:
  new Ajax.Updater('id', url, { method: 'get', parameters: par });
  var anchors = document.getElementsByTagName('a');
    for (var i=0; i<anchors.length; i++)
    {
        var anchor = anchors[i];
        var relAttribute = String(anchor.getAttribute('rel'));
        if (relAttribute.toLowerCase().match('history'))
        {
            Event.observe(anchor, 'click', function(){
              //siakaś funkcja
             });
        }
    }
jQuery:
  $('#id').load(url + par);
  $("a[@rel='history']").click(function(){
    //siakaś funkcja
  });

Od razu widać po której stronie stoi przejrzystość (i jasna strona mocy). Ten drugi przykład może nie być już aktualny, bo ostatnio programiści prototype wzięli się do roboty i co jakiś czas zamieszczają wiadomości o zmianach w API, ale takich kawałków można wkleić dużo więcej. Wszystko idzie w dobrą stronę, ale nie wiem czy uda się w niedalekiej przyszłości uzyskać lekkość jQuery.

Ruby on Rails niestety nie mają wsparcia dla jQuery, ale nie jest tak źle. Selektor elementów w RJS’ach dalej będzie działał, resztę kodu można wrzucać bezpośrednio poprzez `page <<`, a brak helperów? Możnaby je przepisać (i zapewne ktoś już to zaczął robić). Aczkolwiek pojawiają się głosy, że domyślne helpery w RoR są krnąbrne oraz złe. Po części całkiem słusznie. Z drugiej strony w niektórych aplikacjach nie ma sensu robić obsługi bez javascriptu (jakieś rozbudowane interfejsy użytkownika), a podobno używanie event handlerów jest wolniejsze niż zwykły onclick. Jak zwykle wszystko zależy od zastosowań i potrzeb. Ciężkie jest życie programisty. Kiedyś padnę na zawał od takiego nawału trudnych decyzji do podjęcia ;)

Jako dalszą lekturę polecam propagandę na blogu jQuery i doniesienia o nowej wersji, gdzie można podziwiać wyniki zabiegów optymalizacyjnych i sporo nowych mechanizmów. Całkiem niedawno jQuery doczekało się książki. Dużym plusem jest też ogromna ilość rozszerzeń. Lista wygląda imponująco.

Jedyne co mnie ostatnio zaniepokoiło to przesiadka z jQuery na prototype, o której mówi Zbigniew Sobiecki w wywiadzie. Jego doświadczenie widać najlepiej patrząc na blipa, więc jakieś powody musi mieć. Będę musiał to zbadać ;)

W każdym razie w tym momencie moim frameworkiem javascriptowym “of choice” jest jQuery. Jeżeli ktoś zna argumenty przemawiające za pozostaniem przy prototype i script.aculo.us niech się nie krępuje i zostawi komentarz.

Dopisane: Znalazłem wczoraj fajne narzędzie do benchmarków. Niestety wersje bibliotek nie są najnowsze, co owocuje cienkimi wynikami jQuery, ale na szczęście jest link do źródeł, więc można wrzucić swoje wersje (ostatnie stabilne wersje jQuery i prototype). Jak widać na obrazkach twórcy jQuery włożyli ogromną ilość pracy w optymalizację, ale cały czas jeszcze trochę brakuje, żeby dogonić prototype.

Rozmawiałem wczoraj ze sztywnym na blipie (jest teraz jednym z developerów blipa). Zwrócił mi uwagę na parę rzeczy, które mi umknęły. Jedną z nich jest brak rozszerzeń dla obiektów istniejących już w javascripcie. Prototype udostępnia bardzo dużo metod, wzorowanych głównie na metodach rubiego, które ułatwiają pracę z typami wbudowanymi (wszelkie iteratory typu collect, each, any, all, różne metody dla klasy String, hashe itp. itd.). Szczerze mówiąc nawet nie pomyślałem, że nic podobnego nie ma w jQuery – przerzuciłem się dosłownie parę dni temu i nie pisałem nic większego. W takim momencie trzeba zadać sobie pytanie co jest priorytetem w danym projekcie. Oczywiście warto znać obie biblioteki, ale niestety do danego zadania trzeba wybrać jedną z nich (teoretycznie nic nie stoi na przeszkodzie, żeby załączyć obie, dzięki jQuery.noConflict, ale trzeba się liczyć z dodatkowymi kilobajtami).

Przejrzałem dodatkowo bloga prototypa i pojawia się cały czas sporo fajnych rzeczy. Myślę, że przy wyborze między tymi dwoma bibliotekami trzeba pomyśleć czy bardziej przydatne będą efekty wizualne i ogólnie grzebanie w CSS i DOM, czy stawiamy na pisanie różnych funkcji “niższego poziomu”. Najgorsze jest to, że wydaje mi się, że w projekcie, który zaczynam będzie się to rozkładało mniej więcej po połowie. Coś czuję, że wrócę jeszcze do tego tematu.

Posted in  | Tags , , ,  | 4 comments

Kilka przydatnych linków

Posted by Piotr Sarnacki Fri, 24 Aug 2007 23:17:00 GMT

Dawno nic nie pisałem, przez co mogło się niektórym wydawać, że blog umarł śmiercią naturalną. Bądź też ja umarłem (śmiercią niekoniecznie naturalną). Jak można dość łatwo zauważyć myśli o owych śmierciach (dwóch! chwila nieuwagi i sytuacja ociera się o żałobę narodową) były mocno przesadzone.

Sytuacja taka jest spowodowana wieloma czynnikami, które sprowadzają się do jednego słowa: zmiany. W skrócie: rzuciłem pracę, prawdopodobnie rzucam studia, siedzę właśnie w Rybniku i razem z paroma osobami zaczynam tworzenie pewnej netowej aplikacji. O której więcej na pewno napiszę w (nie)dalekiej przyszłości.

Na dobry początek reanimacji ledwo dychającego już blogusia tytułowe przydatne linki:
  • Toggl – podczas pracy, szczególnie w domu, można bardzo łatwo zacząć zajmować się tysiącem rzeczy, które przerwą kodzenie. Toggl jest serwisem, który pozwala zmierzyć realny czas pracy. Dodaję nowy projekt, klikam “start” i zegar zaczyna tykać. I tyka tak sobie licząc godziny pracy. Od kilku dni takie liczenie pomaga mi bardzo w skupieniu nad pisaniem. Jak już chcę kliknąć na zakładkę z google readerem, to zaraz przychodzi myśl, że będę musiał przystopować toggl i realny czas pracy będzie dużo mniejszy.
  • jQuery doczekało się kolejnej już wersji. Jak zwykle dużo nowych bajerów i jeszcze szybsze działanie (a po ostatnim “800% faster” myślałem, że dużo więcej nie wyciągną). Właściwie to na jQuery przesiadłem się dosyć niedawno i jako były-wielki-fan™ script.aculo.us czuję się zobligowany do napisania artykułu o tym co otworzyło mi oczy. Beware!
  • Mashable przygotowało spis narzędzi do google readera. Wreszcie jakaś lista “x tools for…”, która mi się przydała. Polecam skin w stylu OS X. Naprawdę cieszy oko. Aż chce się zacząć przeglądać pomimo straszącego 100+

Na dzisiaj tyle. Oby to nie był ostatni post w przeciągu kolejnych kliku miesięcy. Trzymajcie kciuki.

Posted in  | Tags , , , , ,  | no comments | no trackbacks


Clicky Web Analytics