Pokazywanie postów oznaczonych etykietą html5. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą html5. Pokaż wszystkie posty

czwartek, 28 marca 2013

[HTML|JS|CSS] HTML5: WebSQL

WebSQL jest jedną z tych funkcjonalności, które pomimo iż kojarzone są ze standardem HTML5, nie znajdą się w tym standardzie. Mimo wszystko wiele przeglądarek wspiera możliwość składowania danych w relacyjnej bazie po stronie klienta. API dostępne z JavaScript pozwala na pisanie zapytań w języku SQL, a dane składowane są w pliku (podobnie jak w SQLite). Jeżeli chodzi o kompatybilność z nowoczesnymi przeglądarkami, to problem pojawia się w przypadku Firefoxa i Explorera, co wyklucza sporą część użytkowników (link).

Aby połączyć się z bazą wykorzystujemy funkcję openDatabase:

var db = openDatabase('mydb', '1.0', 'my first database', 2 * 1024 * 1024);

Jeżeli baza nie istnieje to zostaje automatycznie utworzona. Instrukcje SQL wykonywane są w funkcjach zapewniających tranzakcyjność (jeżeli którekolwiek z zapytań się nie powiedzie, cała operacja zostanie cofnięta).

$("#create").click(function(){
 db.transaction(function (tx) {
  tx.executeSql('CREATE TABLE messages (id unique, text)');
 });
});

Tranzakcje można zagnieżdżać, gdyż w callbackach odczytujemy wyniki zapytań.

$("#store").click(function(){
 var value = $("#msg").val();
 db.transaction(function (tx) {
  tx.executeSql('SELECT * FROM "messages"', [], function(tx,results){
   var i = results.rows.length + 1;
   tx.executeSql('INSERT INTO messages (id, text) VALUES (?,?)',
    [i, value]);
  });
 });
 $("#msg").val("");
});

Dane, które chcemy wyświetlić, można filtrować zgodnie ze składnią SQL.

$("#restore2").click(function(){
 var items = $("#items");
 items.html("");
 db.transaction(function (tx) {
  tx.executeSql('SELECT * FROM "messages" WHERE LENGTH(text) > 5', 
   [], function(tx,results){
   var l = results.rows.length;
   for (var i = 0; i < l; i++) {
    var item = results.rows.item(i);
    var li = $("<li>");
    $(li).html(item.id + ", " + item.text);
    items.append(li);
   };
  });
 });
});

Usuwanie z tabeli wykonuje się oczywiście przy użyciu instrukcji DELETE.

$("#removeLast").click(function(){
 var value = $("#msg").val();
 db.transaction(function (tx) {
  tx.executeSql('SELECT * FROM "messages"', [], function(tx,results){
   var i = results.rows.length;
   var sql = 'DELETE FROM "messages" WHERE "id" = ' + i;
   tx.executeSql(sql);
  });
 });
});

Na koniec warto pamiętać, że podobnie jak w przypadku IndexedDB, możemy łatwo oglądać stan naszych tabel w przeglądarce Google Chrome.

niedziela, 24 lutego 2013

[HTML|JS|CSS] HTML5: IndexedDb

Ciekawym trendem, jaki można zaobserwować w standardzie HTML5 jest możliwość składowania coraz większej ilości danych po stronie klienta. Przestarzałe ciasteczka zastępowane są przez znacznie lepszy mechanizm localStorage. To jednak nie wszystko. Pojawia się także możliwość składowania danych w dokumentowej bazie IndexedDb. Podobnie jak w WebStorage, dane zapisywane są dla danej strony internetowej i tylko z jej poziomu mogą być odczytane. Tym razem możemy jednak składować całe obiekty JavaScriptowe, a nie tylko ciągi znaków. Co więcej, zgodnie z dokumentacją IndexedDb, pojemność takiej bazy praktycznie ograniczona jest jedynie miejscem na dysku. Same zapytania wykonywane są w sposób asynchroniczny, programista zatem musi ich wynik obsłużyć w callbackach.

Pracę z indexedDb, technologią mocno eksperymentalną, nad którą prace cały czas trwają, warto rozpocząć od  przypisania sobie dla wygody każdej z możliwych implementacji bazy do jednej zmiennej.

window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;

Jak widać ostatnim silnikiem przeglądarek, który nie wspiera indexedDb jest Presto, na którym tworzona była Opera (obecnie opera ma także być tworzona w oparciu o webkit, więc problem znika dla nowszych wersji).

Zapisywanie obiektów do bazy odbywa się poprzez odpowiednie callbacki.

$("#store").click(function(){
 var value = $("#msg").val();
 var request = indexedDB.open("MyDb", 1); //1
 request.onsuccess = function (evt) {
  db = request.result;
  var transaction = db.transaction("messages", "readwrite"); //2
  var objectStore = transaction.objectStore("messages"); //3                   
  var req = objectStore.add({ date: new Date().toLocaleString(), message: value });
  req.onsuccess = function (evt) {
   console.log('successfully saved');
  };
 };
 request.onerror = function (evt) {
  console.dir(evt);
 };

 request.onupgradeneeded = function (evt) {
  //...
 };
});

Ważne są trzy linie:
  1. Otwieramy bazę danych o nazwie MyDb, opcjonalnie jako drugi parametr podajemy wersję schematu bazy, do której chcemy się łączyć
  2. Pierwszy argument może być także tablicą i jest listą kontenerów, na których będzie wykonywana transakcja
  3. Żeby dodać obiekt do pojedynczego kontenera potrzebujemy obiekt typu objectStore, o nazwie messages
Powyższy kod sprawdza się pod warunkiem, że mamy już utworzoną bazę i kontener, w przeciwnym wypadku wywołana zostanie funkcja onupgradeneeded. 

request.onupgradeneeded = function (evt) {
 var objectStore = evt.currentTarget.result.createObjectStore("messages", 
                             { keyPath: "id", autoIncrement: true 
                         });

    objectStore.createIndex("date", "date", { unique: false });
    objectStore.createIndex("message", "message", { unique: false });
};

Tworzymy objectStore o nazwie messages, posiadający unikalny autoinkrementujący się identyfikator. Dodatkowo, na potrzeby wyszukiwania, można utworzyć dwa indeksy.

Odczyt danych

Podczas odczytu wykorzystuje się indeksy, zwracające wartość i wskaźnik na następny element z kolekcji.

$("#restore").click(function(){
 var request = indexedDB.open("MyDb");
 request.onsuccess = function(evt){
  var db = request.result;
  var transaction = db.transaction("messages");
  var objectStore = transaction.objectStore("messages");
  var req = objectStore.openCursor();
  req.onsuccess = function(evt){
   var cursor = evt.target.result;
   if(cursor){
    var obj = cursor.value;
    var li = $("<li>");
    li.html(obj.date + ", " + obj.message);
    items.append(li);
    cursor.continue();
   }
  }
 }
});

Usuwanie

Korzystając z unikalnych indeksów, usuwanie staje się bardzo proste.Na przykład dla elementu o indeksie 1


$("#removeFirst").click(function(){
 var request = indexedDB.open("MyDb");
 request.onsuccess = function (evt) {
  db = request.result;
  var req = db.transaction(["messages"], "readwrite")
            .objectStore("messages").delete(1);
        }
});

Na koniec warto wspomnieć o tym, jak wygodnie można oglądać stan bazy w przeglądarce Google Chrome, w zakładce Resources.

piątek, 22 lutego 2013

[HTML|JS|CSS] HTML5: WebSockets

Poprzedni post wprowadził do tematyki WebSocketów oraz pokazał, jak prosto zaimplementować je po stronie serwerowej. Tym razem czas na stronę kliencką, gdzie dzięki API HTML5 możemy w prosty sposób wysyłać i odbierać wiadomości. Kluczową funkcją konstruktora jest WebSocket, gdzie jako parametr podaje się URI serwera z gniazdem. Do dyspozycji mamy cztery zdarzenia, do których należy dopisać callbacki. Są to:

  • open
  • message
  • error
  • close
Do komunikacji z serwerem mamy funkcje send i close.

var close = document.getElementById("close");
var send = document.getElementById("send");

socket = new WebSocket("ws://localhost:9001", "echo-protocol");

socket.addEventListener("open", function(event) {
  status.textContent = "Connected";
});

socket.addEventListener("message", function(event) {
  var element = document.createElement('li');
  element.innerHTML = event.data;
  ul.appendChild(element); 
});

socket.addEventListener("error", function(event) {
  alert('Error');
});

socket.addEventListener("close", function(event) {
  status.textContent = "Not Connected";
});


close.addEventListener("click", function(event) {
  message.textContent = "";
  socket.close();
});

send.addEventListener("click", function(event) {
  socket.send(text);
  text.value = "";
});

środa, 31 października 2012

[HTML|JS|CSS] HTML5: Microdata

Ideą, która przyświecała twórcom mikrodanych jest zaszycie w znacznikach html informacji o pewnych encjach danych. Funkcjonalność taka, jest możliwa dzięki temu, że HTML5 ignoruje atrybuty których nie zna, co nie powoduje utrudnień w działaniu strony.  Dane  z jednej strony są przedstawiane użytkownikom odwiedzającym strony, z drugiej dostępne są do pobrania w formie klucz - wartość.  W związku z tym wymagane jest podanie adresu URL do opisu poszczególnych properties danego typu. Same atrybuty mikrodanych opisano poniżej:
  • itemscope - definicja kontenera na mikrodane
  • itemtype - adres URL z opisem poszczególnych pól danego typu, typy można zagnieżdżać
  • itemprop - klucz atrybut (wartość przeważnie pomiędzy tagiem otwierającym a zamykającym)

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Microdata</title>
</head>
<body>
  <div itemscope itemtype="http://www.data-vocabulary.org/Person/">
 <div>
   <span>Hello, my name is <strong itemprop="name">John Doe</strong></span>
 </div>
 <div>
   <span>I work as a <strong itemprop="role">Software Developer></strong> and I study at  <strong itemprop="affiliation"> AGH UST</strong>
 </div>
  </div>
</body>
</html>

[HTML|JS|CSS] HTML5: Web Workers

Web Workers to funkcjonalność, która umożliwia wykonywanie długotrwałych operacji w tle. Główną korzyścią jest więc responsywny interfejs użytkownika podczas wykonywania tych operacji. Kilka wad, jakie pojawiają się przy korzystaniu z WW, to przede wszystkim ograniczenia, a pośród nich brak dostępu do:
  • DOM
  • Window
  • Host page
Kolejnym problemem może być fakt, że kod wykonywany przez workera powinien być pisany tylko w JavaScripcie. W związku z tym nie możemy korzystać z popularnych bibliotek jak na przykład jQuery.  Pomimo tych problemów, wciąż mamy dostęp do takich mechanizmów, jak:
  • navigator
  • timeout
  • XHR
Aby skorzystać z Web Workera musimy użyć funkcji konstruktora Worker, komunikacja odbywa się poprzez funkcję postMessage, gdzie dane przekazuje się jako argumenty. Aby przerwać działanie wystarczy wywołać funkcję terminate().

Poniżej przykład obliczania w tle silni dla zadanej ilości liczb.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Web workers</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
 <h3>Factorial calculator</h3>
 <div>
  <span>
   <input type="number" id="count" />
   <button id="start">Start</button>
   <button id="terminate">Terminate</button>
  </span>
 </div>
 <div>
  <ol id="list">
  </ol>
 </div>
</body>
</html>

Plik ze skryptem:

var worker;

$(function(){
 $('#start').click(function(){
  $('#list').html("");
  var count = $('#count').val();
  var settings = {};
  settings.count = parseInt(count);
  worker = new Worker('factorial.js');
  worker.onmessage = function(event){
   var list = event.data;
   $.each(list, function(){
    printNumber(this);
   })
  };
  worker.postMessage(settings);
 });

 $('#terminate').click(function(){
  if(worker){
   worker.terminate();
  }
 });
});

function printNumber(number){
 $('#list').append('<li>'+number+'</li>');
}

Plik factorial.js

var results = [];

function messageHandler(e){
 if(e.data.count > 0){
  calculatefactorial(e.data.count);
 }
}

function factorial(n){
 if(n < 2){
  return 1;
 }
 else
  return n*factorial(n-1);
}

function calculatefactorial(length){
 for(var i = 1; i <= length; i++){
   results.push(factorial(i));
  }; 
 setTimeout(function(){
  postMessage(results);
 }, i*100);
 
}

addEventListener("message", messageHandler, true);

[HTML|JS|CSS] HTML5: Geolocation

Geolokacja, a więc zdolność do określania położenia geograficznego  jest kolejną funkcjonalnością dostępną w HTML5. Długość i szerokość geograficzną urządzenia klienta uzyskuje się na różne sposoby zależnie od tego, co umożliwia urządzenie, z którego wysyłamy żądanie podania położenia:
  • Adres IP
  • Sieć WiFi
  • Wbudowane urządzenie GPS
  • Bluetooth MAC Address
  • GSM/CDMA telefonu
Sposób określania położenia przekłada się na dokładność, z jaką podane nam zostaną nasze współrzędne geograficzne.

Poniżej przykład, jak w prosty sposób pobrać współrzędne:

$(function(){

 $('#getBtn').click(function(){
  navigator.geolocation.getCurrentPosition(callback,err);
 });
});

function callback(position){
 var coords = position.coords;
 $('#lat').val(coords.latitude);
 $('#lon').val(coords.longitude);
}
function err(){
 alert('An error occured');
}

Przed pierwszym pobraniem przeglądarka zapyta, czy zezwolić na pobieranie danych na temat współrzędnych.


API udostępnia kilka opcji, które możemy podać jako ostatnim argument:
  • High Accuracy (bool, default:false) - może poprawić dokładność kosztem dłuższego oczekiwania
  • Timeout (int [ms], default no value) - czas po którym strona ma zaprzestać obliczeń.
  • Maximum Age (int[ms], default: 0) - w trybie ciągłego śledzenia położenia jest to czas, po którym ma się ponownie pobrać informacja o współrzędnych.

piątek, 26 października 2012

[HTML|JS|CSS] HTML5: Web storage

Do tej pory popularną metodą przechowywania danych po stronie klienckiej były tzw, cookies. Serwer wysyłał pewne informacje, zapisywane na komputerze klienta i odczytywał je przy ponownym uruchomieniu strony. Mechanizm ten jest nadal szeroko stosowany przy projektowaniu "spersonalizowanych" stron www. Wraz z nadejściem standardu HTML5 dostajemy alternatywę w postaci Web Storage. Funkcjonalność ta umożliwia składowanie danych po stronie klienckiej bez ingerencji serwera. Dodatkowo otrzymujemy znacznie więcej dostępnej pamięci. Wprowadzono dwa rodzaje Web storage, które udostępniają identyczne API dla programistów JS:
  • Local storage
  • Session storage
Oba obiekty to properties obiektu window, więc mamy do nich łatwy dostęp po stronie skryptowej. Różnica polega na tym, że o ile pamięć local storage jest trwała (tzn. musimy ją opróżniać konkretnym poleceniem), o tyle dane zapisane w session storage zostają utracone wraz z utratą sesji (15 minut bezczynności użytkownika, zamknięcie przeglądarki, zamknięcie karty itd).
Aby uruchomić funkcjonalność Web storage należy stronę wystawiać na serwerze (nie wystarczy otworzyć w przeglądarce dokumentu).

Poniżej prosty przykład wykorzystujący API Web storage. Zapis i odczyt można wykonywać na trzy sposoby:
  • localStorage[key] = value
  • localStorage.setItem(key,value)
  • localStorage.key = value


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Web storage</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
 <div>
  <h1>Local storage</h1>
  <span>Key: 
   <input type="text" id="lskey" />
  </span>
  <span>Value: 
   <input type="text" id="lsvalue" />
  </span>
  <span>
   <button id="lswrite">Write</button>
   <button id="lsread">Read</button>
   <button id="lsclear">Clear</button>
  </span>
 </div>
 <div>
  <h1>Session storage</h1>
  <span>Key: 
   <input type="text" id="sskey" />
  </span>
  <span>Value: 
   <input type="text" id="ssvalue" />
  </span>
  <span>
   <button id="sswrite">Write</button>
   <button id="ssread">Read</button>
   <button id="ssclear">Clear</button>
  </span>
 </div>
</body>
</html>

Obsługa API Web Storage:

$(function(){

 //local storage
 $('#lswrite').click(function(){
  var key = $('#lskey').val();
  var val = $('#lsvalue').val();
  localStorage.setItem(key,val);
 });

 $('#lsread').click(function(){
  var key = $('#lskey').val();
  $('#lsvalue').val(localStorage.getItem(key));
 });

 $('#lsclear').click(function(){
  var key = $('#lskey').val();
  var val = $('#lsvalue').val();
  if(!localStorage[key]){
   localStorage.clear();
  }
  else{
   localStorage[key] = "";
  }
 });

 //session storage
 $('#sswrite').click(function(){
  var key = $('#sskey').val();
  var val = $('#ssvalue').val();
  sessionStorage.setItem(key,val);
 });

 $('#ssread').click(function(){
  var key = $('#sskey').val();
  $('#ssvalue').val(sessionStorage.getItem(key));
 });

 $('#ssclear').click(function(){
  var key = $('#sskey').val();
  var val = $('#ssvalue').val();
  if(!sessionStorage[key]){
   sessionStorage.clear();
  }
  else{
   sessionStorage[key] = "";
  }
 });
});

wtorek, 23 października 2012

[HTML|JS|CSS] HTML5: Audio and video

Standard HTML5 wprowadza dwa nowe tagi: <audio> i <video>. Umożliwiają one osadzanie plików multimedialnych w stronach www, zapewniając jednocześnie podstawową funkcjonalność do ich odtwarzania. Jedną z głównych zalet w porównaniu z dotychczasowym sposobem odtwarzania multimediów jest brak zapotrzebowania na technologię Flash, z którą spore problemy występują np. na iPadach.

1. Tag <video>

Aby odtwarzać filmy na naszej stronie, wystarczy dostarczyć plik oraz osadzić poniższy fragment kodu na stronie. 

<video width="320" height="240" controls="controls">
 <source src="Two Steps from Hell - Heart of Courage.mp4" type="video/mp4">   
  Your browser does not support video tag
</video>

Tekst o nie wspieraniu tagu zostanie przykryty w momencie gdy przeglądarka rozpozna tag source. Dodatkowo mamy do dyspozycji kilka użytecznych atrybutów.
  • autoplay - true lub false w zależności od tego, czy film ma startować od razu przy przeładowaniu strony.
  • controls - w zależności od tego, czy występuje pojawia się pasek z kontrolkami obsługującymi film
  • loop  gdy ustawimy na true, film będzie się zaczynał od nowa po zakończeniu odtwarzania
  • poster - obrazek, który będzie wyświetlany, gdy nie zostanie odnaleziony film
  • onerror - zdarzenie błędu, które może być obsłużone w JavaScripcie.

2. Tag <audio>

W bardzo podobny sposób działa tag do odtwarzania plików dźwiękowych. W tym przypadku warto pamiętać o tym, która przeglądarka wspiera które formaty (choć oczywiście sytuacja jest dynamiczna i już wkrótce ten problem może się nie pojawiać). Spis kompatybilności przeglądarek z formatami audio tutaj. Jeżeli chcemy wspierać wiele przeglądarek, można zawsze umieścić źródła do plików w wielu formatach. Przeglądarka wybierze pierwszy wspierany.

<audio controls="controls">
 <source src="Wilderness.ogg" type="audio/ogg">
 <source src="Wilderness.mp3" type="audio/mp3">
 Your browser does not support audio tag
</audio>

Z atrybutów opisanych dla video mamy dostępne wszystkie poza poster.

poniedziałek, 22 października 2012

[HTML|JS|CSS] HTML5: Drag and drop

Jedną z nowości, jakie wprowadza standard HTML5 jest API do drag and drop. Dzięki niemu użytkownik może w obrębie okna przeglądarki przesuwać elementy. Programista dostaje z kolei kilka atrybutów, które może wykorzystać jako callbacki oprogramowane w JavaScript. Wspomniane atrybuty to:
  • draggable
  • ondragenter
  • ondragover
  • ondrop
  • ondragstart
  • ondragend
Pierwszy z nich ustawia się na true na pewnym elemencie DOM, jeśli chcemy zezwolić użytkownikowi na przesuwanie tego elementu. Pozostałe służą do powiązania z nimi odpowiednich funkcji JS. Atrybuty ondragenter oraz ondragover oraz ondrop służą do przechwytywania zdarzeń elementu, nad którym ma mieć miejsce przeciąganie, dwa ostatnie dotyczą samego elementu przeciąganego.

Ostatnim ważnym obiektem jest dataTransfer. Dostęp do niego mamy poprzez zdarzenia związane z drag and drop. Możemy w nim przechowywać dane za pomocą funkcji setData, getData, clearData. W property o nazwie effectAllowed programista może ustawić na jaki efekt zezwala użytkownikowi:
  • move - przeniesienie elementu
  • copy - skopiowanie elementu w miejscu, w którym nastąpi drop
  • link - podlinkowanie elementu poprzez data transfer 
Poniżej obszerny przykład, jak napisać prosty mechanizm drag and drop w HTML5.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Drag and drop</title>
<script type="text/javascript" src="script.js"></script>
<style>
 #target1, #target2, #target3{
  float: left; 
  width: 250px; 
  height: 250px;
  padding: 10px;
  margin: 10px;
  background-color: cyan;
 }
 #draggable1, #draggable2, #draggable3{
  width: 75px;
  height: 70px;
  padding: 5px;
  margin: 5px;
  background-color: orange;  
 } 
</style>
</head>
<body>
 <h1>Drag and drop example</h1>
 <div id="target1"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">1</div>
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">2</div>
  <div id="draggable1" draggable="true"
   ondragstart="return start(event)"
   ondragstop="return stop">3</div> 
 </div>
 <div id="target2"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
 </div>
 <div id="target3"
  ondragenter="return enter(event)"
  ondragover="return over(event)"
  ondrop="return drop(event)">
 </div>
</body>
</html>


Część skryptowa:
1. start przy rozpoczęciu przemieszczania elementu, określany jest dozwolony rodzaj ruchu oraz przechowywane jest jego id.

function start(e){
 e.dataTransfer.effectAllowed = 'move';
 e.dataTransfer.setData("Data", e.target.getAttribute('id'));
 return true;
}

2. enter- pozwala draggowalnym obiektom przemieszczać się na targetami

function enter(e){
 return true;
}

3. over - funkcja ta decyduje o tym, czy dany element może zostać opuszczony nad konkretnym kontenerem

function over(e){
 var dragid = e.dataTransfer.getData("Data");
 var id = e.target.getAttribute('id');
 if(id == "target3" && dragid == "draggable3"){
  return true;
 }
 if(id == "target2" && dragid == "draggable2"){
  return true;
 }
 return false;
}

4. drop - co ma się stać z elementem po opuszczeniu go przez użytkownika

function drop(e){
 var id = e.dataTransfer.getData("Data");
 e.target.appendChild(document.getElementById(id));
 e.stopPropagation();
 return false;
}

5. end - zakończenie drag and drop, czyszczenie dataTransfer

function end(e){
 e.dataTransfer.clearData("Data");
 return false;
}