<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MGdevelop &#187; JavaScript</title>
	<atom:link href="http://mgdevelop.pl/index.php/kategoria/webdeveloping/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://mgdevelop.pl</link>
	<description>Programming is an art form that fights back</description>
	<lastBuildDate>Thu, 07 Jul 2011 16:59:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Uploader plików z wykorzystaniem HTML5</title>
		<link>http://mgdevelop.pl/index.php/2011/06/uploader-plikow-z-wykorzystaniem-html5/</link>
		<comments>http://mgdevelop.pl/index.php/2011/06/uploader-plikow-z-wykorzystaniem-html5/#comments</comments>
		<pubDate>Tue, 21 Jun 2011 17:37:47 +0000</pubDate>
		<dc:creator>Elektryk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[drag&drop]]></category>
		<category><![CDATA[FileReader]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[plik]]></category>
		<category><![CDATA[upload]]></category>

		<guid isPermaLink="false">http://mgdevelop.pl/?p=609</guid>
		<description><![CDATA[Swego czasu problemem dla twórców stron internetowych było wysyłanie plików bez przeładowywania strony. Mieliśmy do wyboru albo użyć apleta flash albo szpecić... <a href="http://mgdevelop.pl/index.php/2011/06/uploader-plikow-z-wykorzystaniem-html5/">Czytaj dalej&#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Swego czasu problemem dla twórców stron internetowych było wysyłanie plików bez przeładowywania strony. Mieliśmy do wyboru albo użyć <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://www.uploadify.com/">apleta flash</a> albo <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://www.openjs.com/articles/ajax/ajax_file_upload/">szpecić swój kod iframami</a>. Czasy na szczęście się zmieniają i powstają nowe technologie które to pozwalają na coraz więcej. Aktualnie nowoczesne przeglądarki udostępniają obiekt<em>File</em> z poziomu kodu JS który w połączeniu z ajaxem pozwala stworzyć uploader plików dorównujący możliwościami tym flashowym lub javowym.<span id="more-609"></span></p>
<h1>Stawiamy cele</h1>
<p>Najpierw postaram się omówić co będę chciał zbudować w tym tutorialu. Moim celem jest napisanie uploadera który będzie w przeglądarkach Chrome i Firefox zastępować tradycyjny HTMLowy formularz. Nasz skrypt bedzie przyjmował pliki metodą <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://pl.wikipedia.org/wiki/Przeci%C4%85gnij_i_upu%C5%9B%C4%87">drag &amp; drop</a> i wyświetlał ich listę w postaci kontenerów. Po wykryciu, że internauta trzyma pliki nad przeglądarką nastąpi zachęcenie go do upuszczenia ich w odpowiednim miejscu poprzez podmianę napisu. Każdy kontener zaś będzie wyświetlał nazwę wybranego pliku, miniaturkę jeśli wybrany plik jest grafiką lub ewentualnie ikonkę zastępczą, pole tekstowe gdzie można dodać opis oraz przycisk wysyłania. Kontener będzie zawierał także przycisk usuwający dany plik z listy do wysłania oraz pole na wyświetlanie komunikatów. W momencie kliknięcia przycisku wysłania sam przycisk zniknie (aby użytkownik nie wysłał kilka razy tego samego pliku) i pojawi się w razie nie udanego uploadu. Każdorazowa próba wysłania spowoduje wyświetlenie odpowiedniego napisu w polu komunikatów. Jeśli chodzi o przesłane pliki to będą one zapisywane pod ustawionym adresem a opisy do nich będą lądować w pliku tekstowym o nazwie <em>&lt;nazwa pliku&gt;.txt</em>.</p>
<p><a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://example.mgdevelop.pl/uploadhtml5/">Wersja demonstracyjna skryptu (nie zapisuje plików na serwerze)</a></p>
<p>Gotowiec do pobrania na dole strony.</p>
<h1>Kod HTML</h1>
<p>Zacznijmy od napisania kodu tradycyjnego formularza:</p>
<pre class="brush:html">&lt;div id="uploadHTML"&gt;
    &lt;form enctype="multipart/form-data" action="uploadHtml.php" method="POST"&gt;
        &lt;input name="upload" type="file" /&gt;
        &lt;input value="Wyślij" type="submit" /&gt;
    &lt;/form&gt;
&lt;/div&gt;</pre>
<p>Ważne jest by objąć tą część kodu w blok z nadaną klasą <em>uploadHTML</em> gdyż później będziemy usuwać stary typ formularz poprzez odwołanie się do tej klasy. Podobny blok tworzymy dla naszego nowoczesnego uploadera:</p>
<pre class="brush:html">&lt;div id="uploadJS" style="display: none"&gt;
    //tu dalsza część kodu
&lt;/div&gt;</pre>
<p>Teraz wrzucamy do niego dwa elementy. Pole gdzie użytkownik ma upuścić plik:</p>
<pre class="brush:html">&lt;div class="uploader" id="uploader"&gt;
    Przenieś tu pliki które chcesz&lt;br /&gt;wysłać na serwer
&lt;/div&gt;</pre>
<p>oraz miejsce na listę plików do wysłania na serwer:</p>
<pre class="brush:html">&lt;div class="uploadList"&gt;
    //tu dalsza część kodu
&lt;/div&gt;</pre>
<p>Do tego diva wstawimy teraz przykładowy element a następnie go schowamy. Będzie to szablon dla skryptu jak mają wyglądać nowe elementy na liście.</p>
<pre class="brush:html">&lt;div id="uploadFileTemplate" class="uploadFile"&gt;
    &lt;div class="header"&gt;
        &lt;div class="name"&gt;Nazwa pliku.jpg&lt;/div&gt;
        &lt;div class="delete"&gt;×&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="thumb"&gt;
        &lt;img src="file.png" alt="Miniaturka" class="image" /&gt;
    &lt;/div&gt;
    &lt;div class="info"&gt;
        &lt;textarea name="description"&gt;&lt;/textarea&gt;
        &lt;p class="alert"&gt;&lt;/p&gt;
        &lt;input type="hidden" name="file" value=""&gt;
        &lt;div id="startUpload" class="submit"&gt;Wyślij plik&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<p>W powyższym nie trzeba raczej niczego tłumaczyć po za tym, że div z id <em>startUpload</em> będzie przyciskiem który rozpocznie upload a zaś plik file.png jest naszą domyślną ikonką. Aby szablon był niewidoczny dla użytkownika w arkuszu CSS piszemy:</p>
<pre class="brush:css">#uploadFileTemplate{
        display: none;
}</pre>
<h1>A teraz Javascript</h1>
<p>Po pierwsze dołączymy do naszego kodu bibliotekę <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://jquery.com/">jQuery</a> ponieważ dla łatwości pisania najlepiej będzie oprzeć nasz skrypt na tym frameworku.</p>
<pre class="brush:javascript">&lt;script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"&gt;&lt;/script&gt;</pre>
<p>Teraz zdefiniujemy parę zmiennym których użycie będę wyjaśniał później oraz napiszemy instrukcję która zapewni nam, że nasz kod nie wykona się za nim wszystkie elementy strony nie będą załadowane.</p>
<pre class="brush:javascript">var ePage;
var eDropPlace;
var eStartUpload;
var filesToUpload;
var newId;
var idi = 0;

$(document).ready(function(){
    //tu dalsza część kodu
});</pre>
<p>Wewnątrz tego kodu umieszczamy teraz instrukcję warunkową która wyświetli nasz uploader w przeglądarkah obsługujących wykorzystywane przez nas technologie:</p>
<pre class="brush:javascript">if (window.File &amp;&amp; window.FileReader &amp;&amp; window.FileList) {
    $("#uploadHTML").remove();
    $("#uploadJS").show();
    ePage               = document.getElementsByTagName('body').item(0);
    eDropPlace          = document.getElementById("uploader");
    //tu dalsza część kodu
}</pre>
<p>Powyższy kod działa w ten sposób, że sprawdzane jest czy przeglądarka obsługuje używane przez nas obiekty <em>File</em>, <em>FileReader</em> oraz <em>FileList</em>. Jeśli tak to usuwamy tradycyjny formularz a wyświetlamy ten nasz. Po tej operacji możemy już pobrać referencje do dwóch ważnych obiektów w naszym kodzie. <em>ePage</em> to cała nasza strona dla której będziemy wykrywali czy użytkownik przeniósł pliki nad okno przeglądarki. <em>eDropPlace</em> natomiast to miejsce gdzie użytkownik ma upuścić pliki. Mogłoby być to to samo miejsce ale w tym tutorialu zrobimy rozróżnienie aby lepiej wytłumaczyć czym to się różni.</p>
<h1>Zdarzenia ondragover i ondragleave</h1>
<p>Teraz poniżej tego (ale przed nawiasem klamrowym) dodamy obsługę zdarzeń. Po pierwsze obsłużmy przeniesienie plików nad przeglądarkę:</p>
<pre class="brush:javascript">ePage.ondragover=function(f){
    eDropPlace.innerHTML="Upuśc plik na to pole";
    f.dataTransfer.dropEffect="copy";
    f.preventDefault();
    return false
};</pre>
<p><em>ondragover</em> to właśnie zdarzenie polegające na tym, że użytkownik zaznaczył w folderze pliki i za pomocą myszki przeniósł je nad okno przeglądarki. Jeśli takie coś wykryjemy to zmieniamy treść napisu oraz zmieniamy kursor na informujący, że będziemy kopiować pliki (w Chrome np. przy kursorze pojawia się napis Kopiuj). Metoda<em>preventDefault()</em> natomiast zapewni nam, że przeglądarka nie zacznie się dziwnie zachowywać np. nie będzie zaznaczany tekst podczas przesuwania kursora nad oknem.</p>
<p>Podobne operacje wykonujemy dla zdarzenia przeciwnego czyli <em>ondragleave</em>:</p>
<pre class="brush:javascript">ePage.ondragleave=function(f){
    eDropPlace.innerHTML="Przenieś tu pliki które chcesz&lt;br /&gt;wysłać na serwer";
    f.dataTransfer.dropEffect="copy";
    f.preventDefault();
    return false
};</pre>
<p>Teraz czas na najważniejszą część kodu która obsłuży upuszczenie plików na okno przeglądarki (zdarzenie <em>ondrop</em>) i doda je do listy.</p>
<h1>Zdarzenie ondrop</h1>
<pre class="brush:javascript">ePage.ondrop=function(g){
    g.preventDefault();
    files = g.dataTransfer.files;
    eDropPlace.innerHTML ="Przenieś tu pliki które chcesz&lt;br /&gt;wysłać na serwer";

    //tu dalsza część kodu
}</pre>
<p>Ponownie skorzystaliśmy z metody <em>preventDefault()</em> gdyż przeglądarki domyślnie po upuszczeniu na nie pliku próbują je wyświetlać lub &#8222;ściągnąć&#8221;. Musimy temu zapobiec aby mógł się wykonać dalszy kod. Gdy to już mamy zapewnione to możemy skorzystać z instrukcji <em>g.dataTransfer.files</em> która zwróci nam tablicę obiektów poszczególnych plików. G zostało nam podane jako argument funkcji która wykona się w momencie zajścia zdarzenia <em>ondrop</em>. A jako, że internauta nie trzyma już plików to nie zajdzie zdarzenie <em>ondragleave</em> więc w powyższym kodzie obsługujemy przywrócenie napisu.</p>
<p>Teraz możemy już sobie iterować po tej tablicy i wyciągać kolejne obiekty plików do uploadu:</p>
<pre class="brush:javascript">for(var i = 0, f; f = files[i]; i++){
    var reader=new FileReader();

    //tu dalsza część kodu

    reader.readAsDataURL(f);
}</pre>
<p>Powyższa pętla przypisuje kolejne obiekty plików do zmiennej f, tworzy nowy obiekt klasy <em>FileReader</em> i wczytuje plik do stworzonego obiektu. I teraz naszym zadaniem jest napisanie funkcji która zostanie wykonana gdy cały plik zostanie wczytany (jest on wczytywany asynchronicznie z powodu opóźnienia jakie może wywołać obciążony dysk twardy). Wykonuje się to za pomocą nietypowej konstrukcji która należy umieścić przed użyciem metody <em>readAsDataURL</em>:</p>
<pre class="brush:javascript">reader.onload = (function(theFile) {
    return function(e) {
        //tu dalsza część kodu
    };
})(f);</pre>
<p>Jakkolwiek dziwnie powyższy kod wygląda to nadszedł czas na napisanie funkcji tworzącego nowe elementy na liście. Na początek musimy przyjąć, że każdemu nowemu kontenerowi będziemy nadawać ID wg schematu: <em>file1</em>, <em>file2</em>, <em>file3</em> itd. Realizuje to poniższy kod:</p>
<pre class="brush:javascript">var newId = "file"+idi; idi++;
$(".uploadList").prepend("&lt;div id=\""+newId+"\" class=\"uploadFile\"&gt;"+$("#uploadFileTemplate").html()+"&lt;/div&gt;");</pre>
<p>Jak widać stworzyliśmy nowy kontener, nadaliśmy mu id i skopiowaliśmy do niego nasz szablon z diva <em>uploadFileTemplate</em>. Nowy element pojawi się na liście plików na samej górze dzięki metodzie <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://api.jquery.com/prepend/">prepend</a> z biblioteki jQuery.</p>
<p>Teraz czas na wypełnienie naszego szablonu i nadaniu zdarzeń dla przycisków:</p>
<pre class="brush:javascript">$("#"+newId+" .header .name").text(theFile.name);

//wrzuca miniatirku zdjęć
if(theFile.type.match(/image.*/))
    $("#"+newId+" .thumb .image").attr("src",e.target.result);</pre>
<p>Obiekt theFile w tym momencie przechowuje informacje i naszym pliku. Pobieramy z niego nazwę pliku (<em>theFile.name</em>) i wpisujemy w odpowiednim miejscu. Dalej sprawdzamy czy nasz plik jest obrazkiem za pomocą <em>theFile.type.match(/image.*/)</em>. Jeśli tak to odczytujemy nasz plik z zmiennej <em>e.target.result</em> i wpisujemy jako atrybut<em>src</em> miniaturki. Plik jest przechowywany w wspomnianej zmiennej w formacie <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://pl.wikipedia.org/wiki/Base64">base64</a>. Polecam kliknąć na tak wyświetloną grafikę PPM i wybrać opcję <em>Pokaż obrazek</em>. Zobaczymy wtedy w pasku adresu jak wygląda grafika w takim kodowaniu.</p>
<pre class="brush:javascript">//zapisuje plik do pola tekstowego
$("#"+newId+" input[name=\"file\"]").val(e.target.result.split(",")[1]);

//obsługa usuwania pliku
$("#"+newId+" .delete").click(function(z){
    $("#"+newId).remove();
});

//obsługa wysyłania
$("#"+newId+" #startUpload").click(function(z){
    trySendFile(newId);
});</pre>
<p>Wracając do naszego szablonu to teraz znowu musimy odczytać nasz plik i zapisać go do pola tekstowego o nazwie <em>file</em>. Teraz jednak przypiszemy tylko sam kod bez informacji o typie kodowania. Przy użyciu javascriptowej metody <a title="Zewnętrzny odnośnik" rel="nofollow external" href="https://developer.mozilla.org/en/Javascript/Reference/Global_Objects/String/split">split</a> wytniemy zbędny w tym przypadku napis <em>data:image/png;base64,</em>. W dalszej części kodu oskryptowujemy przycisk anulowania wysyłania pliku którego kliknięcie spowoduje usunięcie całego kontenera oraz przycisk wysyłania pliku którego kliknięcie wywoła większą funkcję <em>trySendFile</em>.</p>
<p>I to wszystko jeśli chodzi o obsługę zdarzenia <em>ondrop</em>. Czy czegoś nam brakuje? Tak, definicji funkcji&#8230;</p>
<h1>trySendFile</h1>
<pre class="brush:javascript">function trySendFile(eId){
    $("#"+eId+" #startUpload").hide();

    var fileData = $("#"+eId+" input[name=\"file\"]").val();
    fileData = fileData.replace(/\+/gi, '-');
    fileData = fileData.replace(/\//gi, '_');

    //dalsza część kodu
}</pre>
<p>Po kliknięciu przycisku wysyłania pierwsze co musi się stać to schowanie owego guzika. Następnie z pola tekstowego <em>file</em> odczytujemy zakodowany plik i podmieniamy w nim pewne znaki. Ktoś może zapytać po co? Po odpowiedź odprowadzam do <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://pl.wikipedia.org/wiki/Base64">artykułu o base64 na Wikipedii</a> a dokładniej to do akapitu pod tabelką. My będziemy transportować plik w ciągu <em>zmienna1=wartosc1&amp;zmienna1=wartosc2&#8230;</em> więc musimy zadbać by żadne znaki nam nie uszkodziły danych. Dwie linijki z powyższego kodu realizują właśnie podmianę omówioną na Wikipedii.</p>
<p>Teraz możemy w końcu znapisać kod które wyśle wszystkie dane na serwer (np do <em>pliku php/getFile.php</em>):</p>
<pre class="brush:javascript">$.ajax({
    type: "POST",
    url: "php/getFile.php",
    data:   "file="+fileData+
            "&amp;name="+encodeURIComponent($("#"+eId+" .header .name").html())+
            "&amp;description="+encodeURIComponent($("#"+eId+" textarea[name=\"description\"]").val()),
    //tu za chwilę dalszy ciąg kodu
});</pre>
<p>Jak widać używamy tu funkcji <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://api.jquery.com/jQuery.ajax/">ajax</a> z biblioteki jQuery. Jako parametr <em>data</em> podajemy ciąg gdzie kolejno składamy zmienne <em>file</em> zawierającą kod base64, <em>name</em> zawierającą nazwę pliku oraz <em>description</em> zawierającą treść z pola tekstowego. Wartości pól <em>name</em> i <em>description</em> zabezpieczyliśmy funkcją <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://www.w3schools.com/jsref/jsref_encodeURIComponent.asp">encodeURIComponent</a> aby dało się je bezpiecznie przesłać w tej formie. I tu znowu ktoś może zapytać dlaczego nie użyliśmy tej metody wcześniej przy zabezpieczaniu base64. Powód jest prosty. Wspomniana metoda podmienia wszystkie znaki specjalne. Jeśli plik waży kilka megabajtów (np. plik <em>.mp3</em>) to użycie omawianej metody powoduje chwilowe zawieszenie przeglądarki. A, że na szczęście w base64 występują tylko dwa <em>psujaki</em> to o wiele optymalnej jest użyć podmiany metodą <em>replace</em>.</p>
<p>Dane zostały wysłane więc teraz trzeba by było obsłużyć odpowiedź. Za nim to jednak zrobimy napiszmy:</p>
<h1>Kod po stronie serwera</h1>
<p>Tu nie będziemy się rozpisywać. Nie podam tu jakiegoś bardzo użytecznego kodu a tylko skrypt który pozwoli przetestować nam uploader i zrozumieć jak należy odbierać kod base64.</p>
<pre class="brush:php">&lt;?php
    $name = str_replace('..',' ',$_POST['name']);
    $name = str_replace('/',' ',$_POST['name']);
    $name = str_replace('.exe',' ',$name);

    if(!file_exists("files/".$name)){
        //dalszy kod
    } else echo "error!Taki plik już istnieje";
?&gt;</pre>
<p>Jak widać w pliku getFile.php najpierw usuwamy z nazwy pliku podwójne kropki i ukośniki aby zapobiec, że jakiś spryciarz zapisze nam plik po za naszym folderem. Usuwamy także rozszerzenia z plików wykonywalnych dla bezpieczeństwa serwera i osoby która będzie te pliki przeglądać. Gdy to już zrobimy sprawdzamy czy nie ma takiego pliku w folderze do uploadu. Jeśli nie to wykonujemy dalszy kod, w przeciwnym wypadku zwracamy przeglądarce komunikat o błędzie. Komunikaty będziemy zwracać w formacie <em>&lt;typ komunikatu&gt;!&lt;treść komunikatu&gt;</em>. Wspomniany dalszy kod wygląda następująco:</p>
<pre class="brush:php">$file = str_replace('-','+',$_POST['file']);
$file = str_replace('_','/',$file);
$f = fopen("files/".$name, "wb");
fwrite($f, base64_decode($file));
fclose($f);
$f = fopen("files/".$name.".txt", "wb");
fwrite($f, $_POST['description']);
fclose($f);
echo "ok!Plik zapisano";</pre>
<p>Po pierwsze przywracamy podmienione wcześniej znaki. Po drugie otwieramy plik o odpowiedniej nazwie i zapisujemy do niego odkodowaną za pomocą <a title="Zewnętrzny odnośnik" rel="nofollow external" href="http://www.php.net/manual/en/function.base64-decode.php">base64_decode</a>treść pliku. Po trzecie zaś otwieramy plik o nazwie <em>&lt;nazwa pliku uploadowanego&gt;.txt</em> i wrzucamy do niego opis napisany przez internautę. Po czwarte zwracamy komunikat, że wszystko poczło dobrze i po piąte&#8230;</p>
<h1>Piszemy ostatni fragment kodu JS</h1>
<p>Teraz wracamy do naszej niedokończonej funkcji ajaxowej i dopisujemy odbiór komunikatów:</p>
<pre class="brush:javascript">    error: function(xhr, ajaxOptions, thrownError){
            $("#"+eId+" p.alert").text("Błąd połęczenia");
            $("#"+eId+" #startUpload").show();
        },
    success: function(msg){
            $("#"+eId+" #startUpload").show();
            var data = msg.split("!");
            switch(data[0]){
                case "error":    $("#"+eId+" p.alert").text(data[1]);
                                 break
                case "ok":       $("#"+eId+" p.alert").text(data[1]);
                                 $("#"+eId+" #startUpload").remove();
                                 break;
                default:         alert(msg);
                                 break;        
            }
        }</pre>
<p>Funkcja napisana po parametrze <em>error</em> jest wykonywana gdy przeglądarka nie może połączyć się z serwerem lub znaleźć pliku. W takim przypadku wyświetlamy internaucie napis, że <em>Brak połączenia</em> i przywracamy przycisk by mógł spróbować raz jeszcze. Zaś <em>success</em> odnosi się do przypadku gdy serwer zwrócił nam jakąś sensowną odpowiedź. Wtedy także przywracamy przycisk a odpowiedź z serwera (zmienna msg) rozwalamy na dwie części znaną już metodą split. Potem sprawdzamy czy komunikat jest typu error czy ok i wykonujemy odpowiednie operacje: wyświetlamy treść komunikatu i ewentualnie kasujemy przycisk wysyłania jak wszystko poszło ok. Gdyby zaś nie udało nam się rozpoznać typu komunikatu (np. zwrócono nam błąd php) to wyświetlamy całość za pomocć alertu</p>
<h1>Gotowiec</h1>
<p>Wszystkie pliki potrzebne do uruchomienia stworzone uploadera można pobrać <a title="Uploader w HTML5" href="http://example.mgdevelop.pl/uploadhtml5/uploader.zip">stąd</a>.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://mgdevelop.pl/index.php/2011/06/uploader-plikow-z-wykorzystaniem-html5/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Sortowalne elementy z zapisem kolejności</title>
		<link>http://mgdevelop.pl/index.php/2010/07/sortowalne-elementy-z-zapisem-kolejnosci/</link>
		<comments>http://mgdevelop.pl/index.php/2010/07/sortowalne-elementy-z-zapisem-kolejnosci/#comments</comments>
		<pubDate>Fri, 09 Jul 2010 22:36:09 +0000</pubDate>
		<dc:creator>Elektryk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[sortable]]></category>

		<guid isPermaLink="false">http://mgdevelop.pl/?p=354</guid>
		<description><![CDATA[Wyobraźmy sobie sytuację, że mamy na swojej stronie kilka rubryczek ustawione w jednym rzędzie i w każdej jest zawartość która... <a href="http://mgdevelop.pl/index.php/2010/07/sortowalne-elementy-z-zapisem-kolejnosci/">Czytaj dalej&#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Wyobraźmy sobie sytuację, że mamy na swojej stronie kilka rubryczek ustawione w jednym rzędzie i w każdej jest zawartość która zainteresuje inne osoby np: sidebar z listami ostatnich wpisów osobno dla różnych kategorii. I teraz możemy mieć dylemat którą tematykę dać na górę a którą ukryć niżej. Jest jednak rozwiązanie: możemy pozwolić użytkownikom aby sami ustalali kolejność która później będzie przywracana przy każdym wczytaniu strony.<span id="more-354"></span></p>
<h1>jQuery i wtyczki</h1>
<p>Pisanie skryptu zaczynamy od pobrania jQuery i dołączenia do swojego kod, tak samo musimy zrobić z jQuery UI gdyż tam znajduję się plugin Sortable na którym oprzemy nasz skrypt. Gdy już to mamy możemy napisać kod html:</p>
<pre class="brush: html">&lt;div class="box"&gt;
   &lt;div id="m" class="element"&gt;matematyka&lt;/div&gt;
   &lt;div id="f" class="element"&gt;fizyka&lt;/div&gt;
   &lt;div id="c" class="element"&gt;chemia&lt;/div&gt;
   &lt;div id="i" class="element"&gt;informatyka&lt;/div&gt;
   &lt;div id="e" class="element"&gt;ekonomia&lt;/div&gt;
&lt;/div&gt;</pre>
<p>Box to nasz pojemnik z divami które będziemy sortować. Każdy z tych elementów musi mieć inne ID by skrypt mógł je rozpoznawać. Poniżej zaś kod JavaScript który uruchomi sortowanie:</p>
<pre class="brush: javascript">$(".box").sortable({ items: '.element' });</pre>
<p>W tym momencie będzie już działać sortowanie ale po odświeżeniu wszystkie elementy wrócą na swoje miejsce. Aby móc zapisać kolejność będziemy musieli skorzystać z mechanizmu ciastek. Aby ułatwić sobie ich obsługę skorzystamy z dwóch specjalnych funkcji które można skopiować <a title="Funkcje do obsługi ciasteczek" href="http://example.mgdevelop.pl/sortowanieizapis/ciasteczka.js">stąd</a>. Przyda się również wtyczka w jQuery do sortowania elementów DOM. Użyjemy qsort którą można pobrać <a title="pobieranie wtyczki Qsort" href="http://plugins.jquery.com/project/qsort">tutaj</a>. Gdy już wszystko podepniemy do naszej strony możemy zacząć pisać nasz skrypt.</p>
<h1>Szczypta naszego kodu</h1>
<p>Najpierw napiszemy kod do zapisywania kolejności. Aby to zrobić musimy do wcześniej napisanego wywołania funkcji sortable dopisać parę dodatkowych linijek.</p>
<pre class="brush: javascript">$(".box").sortable({
   items: '.element',
   update: function(event, ui){
      var k = $(".box").sortable("toArray");
      var ks = k.toString();
      SetCookie('kolejnosc', ks, 30);
   }
});</pre>
<p>Skorzystaliśmy tu z zdarzenia update które jest wywoływane zawsze gdy zmieniona zostanie kolejność elementów. Podpięta funkcja anonimowa najpierw uruchamia metodę <em>toArray</em> by wtyczka sortable zwróciła tablicę id kolejnych elementów, następnie otrzymana tablica jest serializowana by na końcu otrzymany ciąg został zapisany do ciasteczka.</p>
<p>Teraz pozostał nam jeszcze do napisania skrypt który po wczytaniu strony będzie ustawiał elementy w odpowiedniej kolejności. Oto i on:</p>
<pre class="brush: javascript">if(GetCookie('kolejnosc')){
   var kol = GetCookie('kolejnosc').split(',');
   for(var j=0;j&lt;kol.length;j++){
      $('#'+kol[j]).attr('value',j);
   }
   $('.element').qsort({attr: "value"});
   SetCookie('kolejnosc', kol, 30);
}</pre>
<p>Powyższą instrukcję należy wywołać gdy cała strona jest już załadowana. Skrypt działa następująco: najpierw sprawdzamy czy istnieje ciasteczko, jeśli tak to wyciągamy z niego informacje i deserializujemy je do tablicy. Następnie w pętli kolejnym sortowalnym elementom nadajemy value o wartości takiej jaką powinno zajmować miejsce. Gdy to już mamy zrobione uruchamiamy funkcję qsort i na końcu przedłużamy żywotność ciasteczka.</p>
<p>Cały skrypt  z dokładnymi komentarzami można skopiować <a title="NCały napisany przez nas kod" href="http://example.mgdevelop.pl/sortowanieizapis/naszkod.js">tutaj</a> a strona z gotowym efektem jest do zobaczenia w<a title="Gotowy efekt" href="http://example.mgdevelop.pl/sortowanieizapis/"> tym miejscu</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://mgdevelop.pl/index.php/2010/07/sortowalne-elementy-z-zapisem-kolejnosci/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Jak mądrze dodawać zdarzenia JavaScript</title>
		<link>http://mgdevelop.pl/index.php/2010/07/jak-madrze-dodawac-zdarzenia-javasrcipt/</link>
		<comments>http://mgdevelop.pl/index.php/2010/07/jak-madrze-dodawac-zdarzenia-javasrcipt/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 13:22:57 +0000</pubDate>
		<dc:creator>Elektryk</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[iteracja]]></category>
		<category><![CDATA[onClick]]></category>
		<category><![CDATA[węzły]]></category>
		<category><![CDATA[zdarzenia]]></category>

		<guid isPermaLink="false">http://mgdevelop.pl/?p=242</guid>
		<description><![CDATA[Obecnie wszystkie przeglądarki z powodzeniem obsługują typowe zdarzenia JavaScript. Nie znaczy to jednak, że możemy umieszczać je w kodzie naszej... <a href="http://mgdevelop.pl/index.php/2010/07/jak-madrze-dodawac-zdarzenia-javasrcipt/">Czytaj dalej&#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Obecnie wszystkie przeglądarki z powodzeniem obsługują typowe zdarzenia JavaScript. Nie znaczy to jednak, że możemy umieszczać je w kodzie naszej strony jakkolwiek chcemy.  Dalej bowiem roboty wyszukiwarek analizują strony pomijając wszelkie skrypty. Nieprawidłowe podpinanie zdarzeń może także źle wpłynąć na odbiór aplikacji przez internautę. Jak uniknąć zatem problemów? O tym właśnie będzie ten poradnik.<span id="more-242"></span></p>
<h1>Przykład na linkach</h1>
<p>Do najczęściej używanych zdarzeń JavaScript należą <em>onClick</em>, <em>onMouseOver </em>i <em>onMouseOut</em>. Najprościej dodać je w ten sposób:</p>
<pre class="brush: html">&lt;a href="podstrona.html" onclick="jakas_funkcja(); return false"&gt;Kliknij&lt;/a&gt;</pre>
<p>W takim przypadku mamy wywołanie funkcji, za pomocą <em>return false;</em> blokujemy działanie linka a także w razie braku obsługi JS jesteśmy asekurowani przez odnośnik do pliku <em>podstrona.htm</em>l. Fajnie, ale IE7 strzeli nam focha. Poza tym nie jest to zbyt eleganckie rozwiązanie. Jak zaczniemy dodawać inne zdarzenia może dojść do niepotrzebnego bałaganu w kodzie.  A przecież mamy jeszcze <em>onBlur</em>, <em>onFocus</em>, <em>onChange </em>itd. Jak zatem zrobić by wszyscy byli zadowoleni? Już śpieszę pokazać:</p>
<pre class="brush: html">&lt;a href="podstrona.html" class="odnosnik"&gt;kliknij mnie!&lt;/a&gt;</pre>
<p>Pewnie wielu z was zapyta &#8222;Dobra, a gdzie kod?&#8221;. Właśnie cała sztuczka polega na tym by rozdzielić kod HTML od JavaScript i wpakować ten drugi do sekcji head. Samo zdarzenie zaś podepniemy na podstawie dodanej klasy <em>odnosnik</em>.</p>
<p>Poniższy kod należy wstawić do sekcji head. Działa on bardzo prosto. Najpierw wszystkie istniejące na stronie odnośniki zapisujemy w tablicy <em>elementya</em> a potem w pętli sprawdzamy w każdym czy ma klasę <em>odnosnik</em>. Jeśli tak to podpinamy pod zdarzenie <em>onClick </em>funkcję  <em>jakas_funkcja </em>pamiętając o pominięciu nawiasów po nazwie a także o umieszczeniu return false; na końcu naszej funkcji.</p>
<pre class="brush: javascript">var elementya = document.getElementsByTagName('a');
for(i=0; i &lt; elementya.length; i++) {
     if (elementya[i].className=='odnosnik') elementya[i].onclick = jakas_funkcja;
 }</pre>
<p>Zamiast pisać osobną funkcję możemy także stworzyć tzw. funkcję anonimową którą umieszcza się od razu w powyższym kodzie:</p>
<pre class="brush: javascript">var elementya = document.getElementsByTagName('a');
for(i=0; i &lt; elementya.length; i++) {
if (elementya[i].className=='odnosnik') elementya[i].onclick = function() { alert(';P'); return: false; };
}</pre>
<p>Dodaliśmy zaledwie trzy linijki kodu a mamy pewność, że wszystko będzie działało tak jak trzeba na każdej przeglądarce. W razie nie działania JavaScriptu nic także nie będzie przeszkadzać by adres spod linku normalnie się wyświetlił. Taki coś jest potrzebne przy na przykład tworzeniu Ajaxowych komentarzy. Fajnie jak się dodają bez odświeżania strony ale skrypt musi także obowiązkowo działać gdy użytkownik będzie miał zablokowane wykonywanie skryptów w przeglądarce.</p>
<h1>A bez href?</h1>
<p>Jeśli nasz skrypt nie wymaga href w trybie bez JS to możemy np. upodobnić do odnośników element span:</p>
<pre class="brush: html">&lt;script&gt;
    var elementy = document.getElementsByTagName('span');
    for(i=0; i &lt; elementy.length; i++) {
    if (elementya[i].className=='klikacz') elementy[i].onclick = function() { alert(';P'); return: false; };
}
&lt;/srcipt&gt;
&lt;style&gt;
   .klikacz{
        cursor: pointer;
   }
&lt;/style&gt;
&lt;span class="klikacz"&gt;Do złudzenia jak link&lt;/span&gt;</pre>
<p>W powyższym kodzie zmieniliśmy poszukiwane elementy na span a także za pomocą odpowiedniej klasy spowodowaliśmy, że najeżdzając na niego myszką kursor zamieni się na łapkę, tak jakby był to prawdziwy link.</p>
]]></content:encoded>
			<wfw:commentRss>http://mgdevelop.pl/index.php/2010/07/jak-madrze-dodawac-zdarzenia-javasrcipt/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

