70-480 – Wdrażanie wywołania zwrotnego

70-480

Odbieranie wiadomości z HTML5 WebSocket API; stosowanie kwerendy jQuery w celu realizacji wywołania AJAX; podłączanie zdarzenia; wdrażanie wywołania zwrotnego przy użyciu funkcji anonimowych; obsługa wskaźnika „this”.



Opis zagadnienia zawiera wiele elementów, które na pierwszy rzut oka mogą wydawać się zupełnie różne, ale to co ich łączy, to mechanizm callback. Jest to tak zwana funkcja zwrotna, która rejestrowana jest przez autora kodu do wykonania przez bibliotekę, której używa. Callback wykonywany jest w odpowiednim czasie przez bibliotekę, która nie wie nic o tym co się wydarzy. Wszystko zależy od ciała zarejestrowanej funkcji.

HTML5 WebSocket API

Egzamin 70-480 wymaga od nas znajomości API WebSocket, które pojawiło się wraz z HTML5. Specyfikacja definiuje założenia komunikacji full-duplex, czyli w dwie strony serwer <-> przeglądarka <-> serwer.

Zakładane są cztery typy obsługiwanych callback:

  • onopen – połączenie jest gotowe i można wysyłać wiadomości
  • onmessage – w momencie otrzymania wiadomości z serwera
  • onclose – w momencie zamknięcia połączenia
  • onerror – w momencie wystąpienia błędu

Pierwszym krokiem jest oczywiście utworzenie połączenia:

var connection = new WebSocket('ws://localhost/');

Następnie możemy zdefiniować odpowiednie callback:

connection.onopen = function () {
  connection.send('Ping');
};

connection.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

connection.onclose = function(e) {
  console.log('Closed!');
};

connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

Każdy z callbacków wykona się w ściśle zdefiniowanym momencie. Decyduje o tym API WebSocket oraz komunikacja z serwerem.

Staram się skupiać na podstawach, a zarazem materiale, który w pełni pokrywa założenia egzaminu 70-480. Jeżeli ktoś chce dowiedzieć się nieco więcej na temat HTML5 WebSocket API, to odsyłam do artykułu na html5rocks.com.

jQuery – get, post, ajax

Biblioteki jQuery przedstawiać nikomu nie trzeba. Zyskała ona ogromną popularność społeczności, a to za sprawą wielu ułatwień i funkcji, które zwyczajnie ułatwiają życie. jQuery również świetnie radzi sobie z asynchronicznością oraz obsługą requestów typu AJAX.

Polegają one na wykonywaniu operacji komunikacji z serwerem w tle. Tym samym możemy pobierać dane oraz zmieniać interfejs strony bez przeładowania okna przeglądarki.

Przedstawię niżej trzy najbardziej popularne metody do zapytań typu AJAX, które znajdziemy w bibliotece jQuery.

$.get

Pozwala wykonywać asynchroniczne zapytania typu GET. Sygnatura metody wygląda następująco:

$.get(url [,data] [,success] [,dataType])

, gdzie poszczególne parametry to:

  • url – [String] – url pod jaki ma zostać wysłane zapytanie typu GET
  • data – [PlainObject | String] – obiekt lub string, które wysłane zostają razem z zapytaniem do serwera
  • success – [Function(data, textStatus, jqXHR)] – funkcja i zarazem nasz callback, który wykonany zostanie w momencie poprawnego obsłużenia odpowiedzi. Parametry, które zostaną przekazane do wywołania funkcji to:
    • data – [PlainObject] – zwrócone dane
    • textStatus – [String] – status odpowiedzi
    • jqXHR – [jqXHR] – odpowiednik XMLHTTPRequest
  • dataType – [String] – typ zwracanych danych – xml, json, script, html. Domyślnie ustalany na podstawie zwróconych danych.

Funkcja $.get jest odpowiednikiem (skrótem) dla funkcji $.ajax:

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

$.post

W zasadzie od funkcji $.get różni się tym, że tym razem wysyła na serwer asynchroniczne żądanie typu POST. Zestaw parametrów jest tutaj identyczny, a odpowiednikiem funkcji $.ajax jest:

$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

$.ajax

Funkcja, która zawsze wywoływana jest pod spodem. Jest to najbardziej ogólna funkcja pozwalająca na definiowanie asynchronicznych zapytań. Jej pełna specyfikacja znajduje się na stronie jQuery. Posiada pokaźną liczbę parametrów i opisywanie ich wszystkich tutaj byłoby sporym nadmiarem materiału do przyswojenia.

callback i słowo kluczowe this

W zasadzie o czymś podobnym pisałem już we wpisie o wywoływaniu i obsłudze zdarzeń. Chodzi mianowicie o dostęp do kontekstu this wywołanej funkcji. Spójrzmy na przykład funkcji, która wykonuje callback na sam koniec swojego działania:

function callbackFn() {
  console.log(this);
}

function invoke(x, y, callback) {
  var xy = x + y;
  callback();  
}

invoke(1, 2, callbackFn);

Co będzie wynikiem console.log(this)? Na konsoli otrzymamy obiekt window. Jak przekazać kontekst funkcji wywołującej callback? No to kolejny przykład:

function callbackFn() {
  console.log(this);
}

function invoke(x, y, callback) {
  var xy = x + y;
  callback.call(xy);  
}

invoke(1, 2, callbackFn);

Zmiana nastąpiła w linii 6 poprzez użycie call. Tym razem wyliczona wartość xy została przekazana do funkcji callbackFn i stanowi jej kontekst (this).

Pobranie treści strony niczym Facebook – PHP / jQuery

pobranie-tresci-strony-niczym-facebook-php-jquery

Z pewnością wszyscy widzieli i znają sposób dodawania adresów URL do naszych statusów na Facebooku lub Google+. Mam oczywiście na myśli sytuację, gdy po chwili od wklejenia adresu URL w okno, gdzie tworzymy nasz status sprarsowana zostaje i pobrana treść ze strony, której adresu użyliśmy. Jest to zarazem efektowne i bardzo użyteczne, bo od razu widzimy zajawkę informacji, która kryje się pod danym URL. Pokażę Wam dzisiaj prosty sposób, który wykorzystuje język PHP i bibliotekę  jQuery wraz z zapytaniem Ajax. Już dawno przymierzałem się do podobnego wpisu i wreszcie finalnie mogę go Wam przedstawić.

Zacznijmy od obsługi pola, gdzie użytkownik podaje swój adres URL. Obsługiwać będzie je oczywiście kilka linijek kodu w jQuery, gdzie w pierwszej kolejności sparsować musimy wprowadzoną treść oraz sprawdzić istnienie w niej poprawnego adresu URL, a następnie wykonać zapytanie Ajax do skryptu w języku PHP.

Pobieramy treść

$(document).ready(function() {
    var getUrl = $('#get_url') // url to extract from text field
        match_url = /\b(https?):\/\/([\-A-Z0-9.]+)(\/[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#\/%=~_|!:,.;]*)?/i // url to match in the text field
    ;

    getUrl.keyup(function() { // user types url in text field        
        // continue if matched url is found in text field
        if (match_url.test(getUrl.val())) {
            $("#results").hide();
            $("#loading_indicator").show(); // show loading indicator image

            var extracted_url = getUrl.val().match(match_url)[0]; // extracted first url from text filed

            // ajax request to be sent to extract-process.php
            $.post('extract-process.php', {'url': extracted_url}, function(data) {
                extracted_images = data.images;
                total_images = parseInt(data.images.length - 1);
                img_arr_pos = total_images;

                if (total_images > 0) {
                    inc_image = '<div class="extracted_thumb" id="extracted_thumb"><img src="' + data.images[img_arr_pos] + '" width="100" height="100"></div>';
                } else {
                    inc_image = '';
                }
                // content to be loaded in #results element
                var content = '<div class="extracted_url">' + inc_image + '<div class="extracted_content"><h4><a href="' + extracted_url + '" target="_blank">' + data.title + '</a></h4><p>' + data.content + '</p><div class="thumb_sel"><span class="prev_thumb" id="thumb_prev"> </span><span class="next_thumb" id="thumb_next"> </span> </div><span class="small_text" id="total_imgs">' + img_arr_pos + ' of ' + total_images + '</span><span class="small_text">  Choose a Thumbnail</span></div></div>';

                // load results in the element
                $("#results").html(content); // append received data into the element
                $("#results").slideDown(); // show results with slide down effect
                $("#loading_indicator").hide(); // hide loading indicator image
            }, 'json');
        }
    });
});

Wybieramy miniaturę

Skrypt w PHP “wypluje” nam takie dane jak tytuł, zajawkę oraz zdjęcie(-a) w formacie JSON. Jeżeli zdjęć jest kilka, to byłoby dobrze dać użytkownikowi wybór konkretnej miniatury.  Zróbmy to.

$(document).ready(function() {
    // user clicks previous thumbail
    $("body").on("click", "#thumb_prev", function(e) {
        if (img_arr_pos > 0)
        {
            img_arr_pos--; // thmubnail array position decrement

            // replace with new thumbnail
            $("#extracted_thumb").html('<img src="'+extracted_images[img_arr_pos]+'" width="100" height="100">');

            // replace thmubnail position text
            $("#total_imgs").html((img_arr_pos)+' of '+total_images);
        }
    });

    // user clicks next thumbail
    $("body").on("click", "#thumb_next", function(e) {
        if (img_arr_pos < total_images)
        {
            img_arr_pos++; // thmubnail array position increment

            // replace with new thumbnail
            $("#extracted_thumb").html('<img src="'+extracted_images[img_arr_pos]+'" width="100" height="100">');

            // replace thmubnail position text
            $("#total_imgs").html((img_arr_pos)+' of '+total_images);
        }
    });
});

HTML

Poniżej dosłownie 5 linijek HTML, który pozwoli użytkownikowi na wklejenie swojego aresu URL.

<div class="extract_url">
    <img id="loading_indicator" src="images/ajax-loader.gif" />
    <textarea id="get_url" placeholder="Enter Your URL here" class="get_url_input" spellcheck="false"></textarea>
    <div id="results"></div>
</div>

Czas na skrypt w PHP

Do pobrania treści z innej strony w naszym skrypcie PHP wykorzystamy PHP Simple HTML DOM Parser, który w niezwykle łatwy sposób pozwala sparsować drzewo DOM na podstawie przekazanego URL. Trzeba nadmienić, że wymaga on PHP w wersji 5+, ale chyba nie powinno być to dla nikogo problemem. Wszystkie zebrane dane są oczywiście zwracane w formacie JSON.

<?php
if (isset($_POST["url"])) {
    $get_url = $_POST["url"];

    // include PHP HTML DOM parser (requires PHP 5 +)
    include_once("include/simple_html_dom.inc.php");

    // get URL content
    $get_content = file_get_html($get_url);

    // get page title 
    foreach ($get_content->find('title') as $element) {
        $page_title = $element->plaintext;
    }

    // get body text
    foreach ($get_content->find('body') as $element) {
        $page_body = trim($element->plaintext);
        $pos = strpos($page_body, ' ', 200); // find the numeric position to substract
        $page_body = substr($page_body, 0, $pos); // shorten text to 200 chars
    }

    $image_urls = array();

    // get all images URLs in the content
    foreach ($get_content->find('img') as $element) {
        // validate image name
        if (!preg_match('/blank.(.*)/i', $element->src) && filter_var($element->src, FILTER_VALIDATE_URL)) {
            $image_urls[] = $element->src;
        }
    }

    // prepare for JSON 
    $output = array('title' => $page_title, 'images' => $image_urls, 'content' => $page_body);
    echo json_encode($output); // output JSON data
}
?>

Tym oto sposobem mamy gotowe rozwiązanie! Poniżej link do pobrania całości.

Pobranie treści strony niczym Facebook – PHP / jQuery