Home > HTML 5 > HTML 5 Mobile – Week 3 Summary

HTML 5 Mobile – Week 3 Summary

This is another weekly summary of the live video class HTML 5 Mobile Web Development. I have already written a summary of week 2. This week we are going to use ajax to request live Twitter updates, use some CSS3 transitions, and explore web fonts.

Setup

This week you will need to host your files on a PHP server with curl support enabled. We will be using the following PHP script to act as an ajax proxy when communicating with Twitter. This is done to get around the same origin browser security policy which prevents scripts from accessing a server other than that which hosts the page containing the script. There are other techniques for this which were discussed in some detail in the chat room.

curl.php

<?php
$apiCall = isset($_GET['apiCall']) ? $_GET['apiCall'] : false;
if (!$apiCall) {
    header("HTTP/1.0 400 Bad Request");
    echo "Failed because apiCall parameter is missing";
    exit();
}

$url = urldecode($apiCall);

// Open the Curl session
$session = curl_init($url);
// Don't return HTTP headers. Do return the contents of the call
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// Make the call
$json = curl_exec($session);

// The web service returns JSON
header("Content-Type:  application/json");

echo $json;
curl_close($session);

You can determine whether your server supports the curl extension by looking at the output of this PHP snippet:

<?php phpinfo();

You should see a section titled “curl”.

Section 1

Time to add the javascript necessary to fetch tweets asynchronously. This is standard stuff for current websites and the browser specific details would normally be handled for you by a javascript framework such as jQuery. None of this code is specific to HTML 5.

function pageLoaded() {
    //showProfile('JakeCarter');
    getTweets();
}

function getTweets() {
    //console.log('getTweets()');

    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE && this.status == 200) {
            console.log("Got tweets? Yup!");
            // So far so good.
            // TODO: Add code to convert JSON to JS Object

            // TODO: Loop through results and Create List Items for our list.
        }
    };
    var apiCall = 'http://search.twitter.com/search.json?q=OReillyMedia';
    var requestString = '../../../shared/scripts/curl.php?apiCall=' + escape(apiCall);

    client.open('GET', requestString, true);
    client.send();
}

To view the javascript console, use Firebug in Firefox, or the Web Inspector in webkit browsers (Safari & Chrome/Chromium). Here’s what the page looks like in Chromium.

Screenshot 01

Section 2

Now we need to actually parse the data returning from Twitter so that we can display it appropriately on our page. Because a JSON string is valid javascipt, we can use the javascript built-in eval() function to convert it directly to the equivalent javascript objects. In any kind of production system, you should not use eval() and instead use a proper JSON parser as otherwise you have a security risk of running untrusted or unintentional code. The rest of the new code simply creates some new DOM elements for displaying the twitter data and adds them to the page.

function getTweets() {
    //console.log('getTweets()');

    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE && this.status == 200) {
            // So far so good.
            var tweetsList = document.getElementById('tweetsList');
            var response = eval('(' + this.responseText + ')');
            for (var resultId in response.results) {
                var result = response.results[resultId];

                // Get info we'll use.
                var name = result.from_user;
                var text = result.text;
                var imageUrl = result.profile_image_url;

                // Create Image Element
                var imageElement = document.createElement('img');
                imageElement.setAttribute('src', imageUrl);
                imageElement.setAttribute('alt', name)
                imageElement.setAttribute('class', 'user-image');
                // imageElement.setAttribute('onClick', "showProfile('" + name + "')");

                // Create username span
                var usernameSpan = document.createElement('span');
                usernameSpan.setAttribute('class', 'username');
                usernameSpan.innerText = name;

                // Create message span
                var messageSpan = document.createElement('span');
                messageSpan.setAttribute('class', 'message');
                messageSpan.innerText = text;

                // Create container div
                var div = document.createElement('div');
                div.appendChild(imageElement);
                div.appendChild(usernameSpan);
                div.appendChild(messageSpan);

                // Create List Item
                var li = document.createElement('li');
                li.appendChild(div);

                // Add List Item to List
                tweetsList.insertBefore(li, tweetsList.firstChild);
            }
        }
    };

Here’s what it looks like on an Android device:

Screenshot 02

Section 3

Now that we’re getting back real tweets, let’s add a click handler to display detailed profile information. It will make another call to Twitter.

// Create Image Element
var imageElement = document.createElement('img');
imageElement.setAttribute('src', imageUrl);
imageElement.setAttribute('alt', name)
imageElement.setAttribute('class', 'user-image');
imageElement.setAttribute('onClick', "showProfile('" + name + "')");
function getProfileForScreenName(screenName) {
    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE && this.status == 200) {
            var profile = eval('(' + this.responseText + ')');

            // Get HTML Elements.
            var profileImage = document.getElementById('profileImage');
            profileImage.setAttribute('src', profile.profile_image_url);
            profileImage.setAttribute('alt', profile.name);

            var name = document.getElementById('name');
            name.innerText = profile.name;

            var screenName = document.getElementById('screenName');
            screenName.innerText = "@" + profile.screen_name;

            var location = document.getElementById('location');
            location.innerText = profile.location;

            var statusText = document.getElementById('statusText');
            statusText.innerText = profile.status.text;

            var followersCount = document.getElementById('followersCount');
            followersCount.innerText = "Followers: " + profile.followers_count;

            var friendsCount = document.getElementById('friendsCount');
            friendsCount.innerText = "Following: " + profile.friends_count;

            switchToSectionWithId('profile');
        }
    };
    var apiCall = 'http://api.twitter.com/1/users/show.json?screen_name=' + screenName;
    var requestString = '../../../shared/scripts/curl.php?apiCall=' + escape(apiCall);

    client.open('GET', requestString, true);
    client.send();
}
function showProfile(username) {
    clearAllNavItems();
    getProfileForScreenName(username);
}

While we’re at it, we can use a CSS3 transition to animate the display of the profile view. This transition will apply to the opacity of the section elements.

section {
    -webkit-transition-property: opacity;
    -webkit-transition-delay: 0.5s;
    -webkit-transition-duration: 0.5s;
    -webkit-transition-timing-function: ease;
    z-index: 0;
    position: absolute;
    top: 88px;
    width: 100%;
    background-color: #ffffff;
    display: block;
    opacity: 0;
}
section.selected {
    z-index: 100;
    opacity: 1;
}

Here’s what the profile view looks like on the device.

Screenshot 03

Final Section

Now let’s use a webfont for a custom look. Before webfonts, you could only specify CSS fonts which you hoped the user had installed. Now we can specify a custom font and provide the file for the browser to use. At the moment, this only works in the Android browser, not the iPhone.

@font-face {
    font-family: Chunk;
    src: url('../../../shared/fonts/chunk/Chunk.otf') format('opentype');
}
/*
    Header
*/
header {
    width: 100%;
    height: 44px;
    background-image: -webkit-gradient(
        linear,
        0% 0%,
        0% 100%,
        from(#666666),
        to(#666666),
        color-stop(.5,#333333)
    );
    font-family: Chunk, Helvetica;

    -webkit-box-sizing: border-box;

Here’s the page at the end of week 3 with the custom font.

Screenshot 04

Useful Links

Class source code download: http://examples.oreilly.com/0636920014225
Class forum: http://forums.oreilly.com/category/104/HTML5-Mobile-Web-Development/

These came up in the chat channel during this week’s session

Categories: HTML 5 Tags: , , ,
  1. Bonjo
    October 21st, 2010 at 01:53 | #1

    Great! Thank you for your effort!

  1. No trackbacks yet.