Specyfikacja obiektowego modelu dokumentu W3C DOM wprowadza nowoczesny sposób obsługi zdarzeń. Niestety model ten nie jest dostępny w przeglądarce Internet Explorer (nawet w najnowszych wersjach) i aby go wykorzystać należy dodać alternatywny sposób jego obsługi.
Funkcja dodająca zdarzenie korzysta z metody addEventListener dla przeglądarek zdognych z DOM oraz attachEvent w przypadku Interet Explorer'a:

JavaScript:
  1. function addEvent(obj, type, fn) {
  2.     if(obj.addEventListener){
  3.         obj.addEventListener(type,fn,false);
  4.         return true;
  5.     } else if(obj.attachEvent) {
  6.         obj['e'+type+fn] = fn;
  7.         obj[type+fn] = function(){obj['e'+type+fn](window.event);}
  8.         var r = obj.attachEvent("on"+type,obj[type+fn]);
  9.         return r;
  10.     } else {
  11.         return false;
  12.     }
  13. }

Funkcja usuwająca zdarzenie korzysta z metody removeEventListener dla przeglądarek zdognych z DOM oraz detachEvent w przypadku Internet Explorer'a:

JavaScript:
  1. function removeEvent(obj,type,fn) {
  2.     if(obj.removeEventListener) {
  3.         obj.removeEventListener(type,fn,false);
  4.         return true;
  5.     } else if(obj.detachEvent) {
  6.         var r = obj.detachEvent('on'+type,obj[type+fn]);
  7.         obj[type+fn] = null;
  8.         return r;
  9.     } else {
  10.         return false;
  11.     }
  12. }

Aby programowo wywołać dodane zdarzenie należy skorzystać z metody disptachEvent dla przeglądarek zgodnych z modelem DOM oraz fireEvent dla Internet Explorer'a:

JavaScript:
  1. function triggerEvent(obj, type) {
  2.     if(obj.dispatchEvent) {
  3.         var evt = document.createEvent("MouseEvents");
  4.         evt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  5.         return obj.dispatchEvent(evt);
  6.     } else if(obj.fireEvent) {
  7.         return obj.fireEvent('on'+type);
  8.     } else {
  9.         return false;
  10.     }
  11. }

Podczas tworzenia stron często zachodzi potrzeba przetestowania ich działania w różnych przeglądarkach. O ile w przypadku przeglądarek innych niż Internet Explorer nie jest to problemem bo można zainstalować dowolną ilość ich wersji to w przypadku wspomnianego Internet Explorera nie da się tego osiągnąć w standardowy sposób. Próba instalacji nowszej wersji zakończy się uaktualnieniem do najnowszej wersji a próba instalacji wersji starszy zakończy się niepowodzeniem.

Na szczęście problem został dostrzeżony i rozwiązany w sposób bardzo wygodny i elegancki. Mamy możliwość pobrania i zainstalowania dowolnej pojedynczej wersji Internet Explorera lub instalacji pakietu Multiple IE gdzie możemy wybrać, które wersje Internet Explorera chcemy zainstalować. Pakiet ten zawiera wersje 3, 4.01, 5.01., 5.5 i 6 i poszczególne wersje IE działają bez żadnych problemów jako samodzielne aplikacje.

Często zachodzi potrzeba zapisania pewnych danych w przeglądarce klienta aby przyśpieszyć działanie serwisu bądź mieć możliwość wykorzystania tych danych po ponownym wejściu odwiedzającego na stronę. Można w tym celu wykorzystać ciasteczka, jednak ze względu na niewielki rozmiar danych, które można tam zapisać nie są one zbyt efektywnym sposobem przechowywania danych po stronie klienta. Znacznie lepszym sposobem jest skorzystanie z obiektów do tego przeznaczonych czyli globalStorage w przeglądarce Firefox 2 i userData w przeglądarce Internet Explorer 5+.

globalStorage w Firefox 2

W przypadku przeglądarki Mozilla Firefox 2 mamy do dyspozycji obiekty sessionStorage i globalStorage. Oba obiekty umożliwiają zapisywanie dowolnych danych z pamięci przeglądarki z tą różnicą, że pierwszy z nich usuwa dane po wygaśnięciu sesji czyli po zamknięciu okna przeglądarki a w przypadku drugiego okres ważności danych jest nieskończony. Ze względu na przydatność zajmę się tylko obiektem drugim czyli globalStorage. Maksymalna ilość danych w nim zapisanych wynosi 5 MB i właśnie tyle mamy do wykorzystania. Dane zapisywane są w pliku webappsstore.sqlite w katalogu profilu Firefoksa. Wykorzystując obiekt globalStorage należy zdefiniować domenę dla jakiej dane mają być dostępne. Można użyć zarówno pełnej nazwy domeny (np. bukox.pl), tylko końcówki domeny (np. pl) lub pustego ciągu oznaczającego, iż dane będą widoczne z każdej domeny. Przykład użycia:

JavaScript:
  1. store = globalStorage['bukox.pl'];
  2. // zapis danych do obiektu
  3. store.addItem('zmienna','wartosc');
  4.  
  5. // odczyt danych z obiektu
  6. alert(store.getItem('zmienna').value);
  7.  
  8. // usuniecie danych z obiektu
  9. store.removeItem('zmienna');

Więcej o obiektach storage w FF można znaleźć na stronie:
http://developer.mozilla.org/pl/docs/DOM:Storage
a specyfikacja DOM obiektu znajduje się pod adresem:
http://www.whatwg.org/specs/web-apps/current-work/#storage.

userData w Internet Explorer

Dla przeglądarki Internet Explorer począwszy od wersji 5 istnieje model userData, który także umożliwia zapisywanie danych użytkownika do pamięci przeglądarki. Rozmiar możliwych do zapisania danych jest zależny od strefy zabezpieczeń. Dla strefy internetu wynosi od 1 MB dla domeny i 128 KB dla dokumentu. Przykład użycia:

Najpierw należy wstawić dowolny znacznik, nadać mu identyfikator i odpowiedni styl:

HTML:
  1. <div id="globalStore" style="behavior: url(#default#userdata)"></div>

Następnie poprzez zwykłe nadanie atrybutu można dodać zmienną i wartość do obiektu:

JavaScript:
  1. // zapis danych do obiektu
  2. globalStore.setAttribute('zmienna','wartosc');
  3. globalStore.save('cache');
  4.  
  5. // odczyt danych z obiektu
  6. globalStore.load('cache');
  7. alert(globalStore.getAttribute('zmienna'));
  8.  
  9. // usuniecie danych z obiektu
  10. globalStore.load('cache');
  11. globalStore.setAttribute('zmienna','');
  12. globalStore.save('cache');

Więcej informacji o obiekcie userData w IE można znaleźć pod adresem:
http://msdn2.microsoft.com/en-us/library/ms531424.aspx.
Przykłady użycia można znaleźć na stronach: http://www.webreference.com/js/column24/userdata.html i http://www.eggheadcafe.com/articles/20010615.asp.

Dla innych przeglądarek póki co nie można wykorzystać podobnych mechanizmów i pozostaje jedynie skorzystanie z ciasteczek lub ograniczenie funkcjonalności tylko do dwóch powyższych. Ze względu na fakt, iż obiekt storage stanowi część specyfikacji DOM można mieć nadzieje, że w najbliższym czasie także pozostałe przeglądarki zostaną wyposażone w obsługę obiektu storage.

Mechanizm cache opisany powyżej umożliwia przechowywanie danych po stronie klienta i ich odczyt nawet po ponownym uruchomieniu przeglądarki. Dzięki temu można stworzyć aplikację internetową, która może działać całkowicie offline. Wystarczy, że podczas normalnej pracy online dynamiczne dane są zapisywane do cache'u a po rozłączeniu z internetem dane są z niego pobierane. Dodatkowo operacje modyfikujące także mogą zostać zapamiętane i po przywróceniu połączenia z internetem dane mogą zostać zsynchronizowane z serwerem.

Poniżej znajduje się kod kompletnej klasy implementującej cache po stronie klienta działający zarówno w IE5+ jak i FF2, który automatycznie tworzy ukrytą warstwę (dla IE) oraz umożliwia kontrolę okresu ważności zapisanych danych.

JavaScript:
  1. var TCache = function(domain) {
  2.     if(typeof globalStorage != "undefined") {
  3.         // jesli FF
  4.         if(typeof domain == "undefined") {
  5.             var domain = location.host;
  6.         }
  7.         this.storage = globalStorage[domain];
  8.     } else {
  9.         // jesli IE
  10.         if(!document.getElementById('globalStore')) {
  11.             // dynamicznie dodanie warstwy dla cache
  12.             var storageObj = document.createElement('DIV');
  13.             storageObj.id = 'globalStore';
  14.             storageObj.style.behavior = 'url(#default#userdata)';
  15.             storageObj.style.display = 'none';
  16.             document.body.appendChild(storageObj);
  17.         }
  18.         this.storage = globalStore;
  19.     }
  20. }
  21. TCache.prototype = {
  22.     /**
  23.      * Magazyn danych
  24.      *
  25.      * @var object
  26.      */
  27.     storage : null,
  28.     /**
  29.      * Funkcja zapisuje cache
  30.      *
  31.      * @param string name   Nazwa zmiennej cache 
  32.      * @param mixed data    Dane do zapisania
  33.      * @param int expires   Okres waznosci danych (w sek., domyslnie bez limitu)
  34.      * @return bool
  35.      */
  36.     save : function(name, data, expires) {
  37.         if(typeof globalStorage != "undefined") {
  38.             // jesli FF
  39.             this.storage.setItem(name, data);
  40.             if(typeof expires != "undefined") {
  41.                 var _this = this;
  42.                 setTimeout(function(){this.remove(name)}, expires*1000);
  43.             }
  44.             return true;
  45.         } else if(typeof this.storage.XMLDocument != "undefined") {
  46.             // jesli IE
  47.             if(typeof expires != "undefined") {
  48.                 var oTimeNow = new Date();
  49.                 oTimeNow.setSeconds(oTimeNow.getSeconds() + expires);
  50.                 this.storage.expires = oTimeNow.toUTCString();
  51.             }
  52.             this.storage.setAttribute(name, data);
  53.             this.storage.save('cache');
  54.             return true;
  55.         } else {
  56.             return false;
  57.         }
  58.     },
  59.     /**
  60.      * Funkcja pobiera dane z cache
  61.      *
  62.      * @param string name   Nazwa zmiennej cache
  63.      * @return mixed
  64.      */
  65.     get : function(name) {
  66.         if(typeof globalStorage != "undefined") {
  67.             // jesli FF
  68.             if(this.storage.getItem(name)) {
  69.                 var result = this.storage.getItem(name).value;
  70.             }
  71.         } else if(typeof this.storage.XMLDocument != "undefined") {
  72.             // jesli IE
  73.             this.storage.load('cache');
  74.             var result = this.storage.getAttribute(name);
  75.         }
  76.         return (result) ? result : null;
  77.     },
  78.     /**
  79.      * Funkcja usuwa zmienna z cache
  80.      *
  81.      * @param string name   Nazwa zmiennej
  82.      */
  83.     remove : function(name) {
  84.         if (typeof globalStorage != "undefined") {
  85.             // jesli FF
  86.             this.storage.removeItem(name);
  87.             return true;
  88.         } else if(typeof this.storage.XMLDocument != "undefined") {
  89.             // jesli IE
  90.             this.storage.load('cache');
  91.             this.storage.setAttribute(name, '');
  92.             this.storage.save('cache');
  93.             return true;
  94.         } else {
  95.             return false;
  96.         }
  97.     }
  98. }

Sposób użycia:

JavaScript:
  1. var cache = new TCache();
  2. cache.save('imie','adam');
  3. alert(cache.get('imie'));