How We Did It - DZone Available On Your iPhone
<li class="pagination">
<a class="next" target="_self" href="?p=2">Page 2</a>
</li>
If you refresh your browser now, you will see at the bottom we have another item added to our list with the same style as the other links except without the zone counter. However, we decided that we wanted to style this a little different and make it stand out more. We will also not only need a next page link but also a previous. With that said add the following to the list item we just added:
<a class="previous" target="_self" href="?p=1" class="next">Page 1</a>
Your pagination list should now look like this:
<li class="pagination">
<a class="previous" target="_self" href="?p=1" class="next">Page 1</a>
<a class="next" target="_self" href="?p=2">Page 2</a>
</li>
Now we need to style these two elements and float the one to the left of the screen and the other to the right. For our container pagination list item we need the following style rule to add some breathing
space as well as remove the bottom border that will be added by default due to the list item rule we defined earlier:
body > ul > li.pagination
{
position: relative;
margin:5px 0;
border-bottom:none;
padding: 8px 0 8px 10px;
font-size: 20px;
font-weight: bold;
list-style: none;
}
For the previous link we need to add the following to our CSS file:
.previous
{
float:left;
background:transparent url('http://dzone.com//links/themes/iphone/media/nav_left_blue_36.png') top left no-repeat;
margin-left:5px;
padding:6px 5px 0 40px;
font-size:110%;
height:36px;
}
And for our next button we need the following:
.next
{
float:right;
background:transparent url('http://dzone.com/links/themes/iphone/media/nav_right_blue_36.png') top right no-repeat;
margin-right:15px;
padding:6px 45px 0 5px;
font-size:110%;
height:36px;
}
With all that added refresh your browser and you should see the following:

With that our landing page is done and we can move on to the story page. One thing to note here is that you will see we made the previous and next images as well as text much larger then the link text.
The reason for this is simply to make it easier for users to interact with the interface by giving a larger hit area then would have been available had we made the icons and text as small as the links. The
links automatically provide a larger hit area as the entire line in width and height is clickable.
Now lets add a story. Initially we created a story.html page and linked to that page to display the content of a story but, in the end we decided to move back to the one page style that most other iPhonized sites
have stuck to. We might change this as the development moves forward or it might not. Anyhow, for the purposes of this article I will stick to the one page concept. Inside your index.html after the closing unordered list tag add the following:
<div id="103776" title="">
<div class="story-body">
<h3><a target="_self" href="http://timeago.yarp.com/">timeago: a jQuery plugin</a></h3>
<p class="details">Timeago is a jQuery plugin that makes it easy to support automatically updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").</p>
</div>
</div>
Make sure that the href of your links link to #103776 to ensure your links work as expected. Go ahead I know you want to. Click on one of the links and see what happens. Pretty awesome I think, I nice sliding transition effect thanks to iUI. Before we add our styling for the story page let's look at how exactly this transition works. At line 150 of the iui.js file located in lib > iUI you will see the following line:
addEventListener("click", function(event)So here is where the event listener is added for click events such as when you clicked on the link. When the click happened this listener was notified and it sprung into action. The first thing we need is the hash of the current node that initiated the click event:
var link = findParent(event.target, "a");
Here we assign the value returned by the function 'findParent' to the variable link. This is how the function looks:
function findParent(node, localName)
{
while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName))
node = node.parentNode;
return node;
}
When we enter this function from the click event that resulted from the anchor link, the first check in the while loop will pass but, our current nodeType is 1 and as our localName is "a" so both conditions inside the brackets will fail causing the script to jump out of the while loop and simply return the node and assign this to the var link. Next we jump over the first if statement but move into the second:
if (link.href && link.hash && link.hash != "#")
{
link.setAttribute("selected", "true");
iui.showPage($(link.hash.substr(1)));
setTimeout(unselect, 500);
}
The fist thing this script does is set the value of the 'selected' attribute to true. If you are stepping through this script you will see that after this line has executed the background color of the link changes to blue. The next line calls the function that creates the sliding transition effect. We call the showPage function passing in the element we are linking from. We call a little utility function to retrieve this element for us based on the id. So in our case we are doing the following:
iui.showPage($(103776));
The utility function looks like this and is pretty self explanatory:
function $(id) { return document.getElementById(id); }This will then return our DIV that contains the story we are linking to. Next we pass this showPage function listed below:
showPage: function(page, backwards)
{
if (page)
{
if (currentDialog)
{
currentDialog.removeAttribute("selected");
currentDialog = null;
}
if (hasClass(page, "dialog"))
showDialog(page);
else
{
var fromPage = currentPage;
currentPage = page;
if (fromPage)
setTimeout(slidePages, 0, fromPage, page, backwards);
else
updatePage(page, fromPage);
}
}
}
When we enter the script here 'backwards' will be undefined and 'page' will contain the content of our DIV. Our first condition passes and we step into the condition block and the next conditional statement. If you look at the top of the uiu.js file you will see that 'currentDialog' is initialized to null and, as we are not triggering a dialog here, this is what it will still be so this condition will fail and we will move to the next condition. This condition will again fail as our current node does not contain a class with the name 'dialog', with that, we step into the else.
As we move into the else block the script's first task is to initialize the variable 'fromPage' with the value of 'currentPage'. Next we overwrite the value of currentPage with the value of our story page. The script next checks whether fromPage is null and if not moves into the setTimeout function calling the slidePages function. Because the call to setTimeout is called with a 0 millisecond wait time the slidePages script will wait to execute until the current call stack has completed it's execution. So from here we return to event listener and the following line is executed:
setTimeout(unselect, 500);
The unselect function looks as follows:
function unselect() { link.removeAttribute("selected"); }So after a 500 millisecond wait, the unselect function will remove our 'selected' attribute and call the following function to stop the default action the browser would have taken for this event:
event.preventDefault();
Having left the event handler the function that will check whether the orientation and/or location has changed and make the needed changes is called. This utility function looks as follows:
function checkOrientAndLocation()
{
if (window.innerWidth != currentWidth)
{
currentWidth = window.innerWidth;
var orient = currentWidth == 320 ? "profile" : "landscape";
document.body.setAttribute("orient", orient);
setTimeout(scrollTo, 100, 0, 1);
}
if (location.hash != currentHash)
{
var pageId = location.hash.substr(hashPrefix.length)
iui.showPageById(pageId);
}
}
For our current scenario both conditional blocks are skipped and the the checkOrientAndLocation function is exited without executing any of the code. When we exit this code block the current call stack is complete and the slidePages function and execute, this is what that function looks like:
function slidePages(fromPage, toPage, backwards)
{
var axis = (backwards ? fromPage : toPage).getAttribute("axis");
if (axis == "y")
(backwards ? fromPage : toPage).style.top = "100%";
else
toPage.style.left = "100%";
toPage.setAttribute("selected", "true");
scrollTo(0, 1);
clearInterval(checkTimer);
var percent = 100;
slide();
var timer = setInterval(slide, slideInterval);
function slide()
{
percent -= slideSpeed;
if (percent <= 0)
{
percent = 0;
if (!hasClass(toPage, "dialog"))
fromPage.removeAttribute("selected");
clearInterval(timer);
checkTimer = setInterval(checkOrientAndLocation, 300);
setTimeout(updatePage, 0, toPage, fromPage);
}
if (axis == "y")
{
backwards
? fromPage.style.top = (100-percent) + "%"
: toPage.style.top = percent + "%";
}
else
{
fromPage.style.left = (backwards ? (100-percent) : (percent-100)) + "%";
toPage.style.left = (backwards ? -percent : percent) + "%";
}
}
}
This is then finally the script that is going to cause the nice transition effect of sliding in our new content instead of either loading the new page or, in our case, jumping to the content as would normally be the case with anchor links. After the first line has executed the var axis is equal to null. This means that our first conditional block will be skipped as 'axis' does not equal 'y', instead 'toPage' which is our story DIV, is given a left position that is 100% from the left of our screen. This will place our DIV just of screen to the right of our link list. The content is given the selected attribute and is set to true after which the content is scrolled up to get it into position for the transition.
When the document was loaded initially we called the checkOrientAndLocation function using setTimeout and assigned the id for this timer to the variable checkTimer, we now use this id to clear that timers interval and prevent the timer from triggering the execution of the script. Next we initialize 'percent' to equal 100 and call the slide() function. The slide function does basically two things for us, with each iteration it decreases the value of percent by 20% and increases the style.left set on 'fromPage' by 20%. This means that after five iteration we would have swapped the links with the story content. One thing to not is that after out first iteration we start a timer to execute the slide function with a wait time of 0, remember what this means.
The last time we execute the slide method and percent is equal to 0 we execute this block:
if (percent <= 0)
{
percent = 0;
if (!hasClass(toPage, "dialog"))
fromPage.removeAttribute("selected");
clearInterval(timer);
checkTimer = setInterval(checkOrientAndLocation, 300);
setTimeout(updatePage, 0, toPage, fromPage);
}
There is some really clever stuff happening here. So, the first thing that iUI does for us after so gracefully sliding the stories content into place is stop the timer that was executing the slide function. Next it re-initializes the checkOrientAndLocation function to execute every 300 milliseconds and saves the id on the checkTimer variable. Finally it starts a new timer calling the updatePage function with a 0 second wait time. So again as before, remember what this means, the current executing stack will complete after which the updatePage function will be executed. When update page is executed this is the code that runs:
function updatePage(page, fromPage)
{
if (!page.id)
page.id = "__" + (++newPageCount) + "__";
location.href = currentHash = hashPrefix + page.id;
pageHistory.push(page.id);
var pageTitle = $("pageTitle");
if (page.title)
pageTitle.innerHTML = page.title;
if (page.localName.toLowerCase() == "form" && !page.target)
showForm(page);
var backButton = $("backButton");
if (backButton)
{
var prevPage = $(pageHistory[pageHistory.length-2]);
if (prevPage && !page.getAttribute("hideBackButton"))
{
backButton.style.display = "inline";
backButton.innerHTML = prevPage.title ? prevPage.title : "Back";
}
else
backButton.style.display = "none";
}
}
Another reason for using iUI is that it assists us in keeping our history intact and allows users to freely use the back button on the phone for navigation purposes if no other means are provided or the user simply prefers this. And it is in updatePage where this happens. The first thing that it does in our case is update the href in the location bar to #_ and the id of our current page, which is currently is 103776. Next is adds this page id to the pageHistory[]. It will then look for a id of pageTitle and set the new document's title to the value of the title of this element or skip this if there is no element with such an id or it is empty. And that is it, the rest is skipped as we are not working with a form or using a link with an id of backButton.
Now that we have our content sliding into view when we click on a link we need to style our content and add some additional fields and a back link to return to our links. First let's style the content already present. Add the following to your CSS style sheet to style the H3 and the paragraph:
.story-body h3
{
margin:10px auto 7px 10px;
font-size:22px;
line-height:1em;
}
.story-body h3 a
{
margin:0;
padding:0;
text-decoration:none;
}
.details, .details p
{
margin:0;
margin-left:10px;
padding:0 5px 10px 0;
font-size:90%;
line-height:140%;
}
Next let's add some more details about the post such as the date, views, submitter etc. For that we will use an unordered list so add the following to the story body DIV:
<ul class="news-details">
<li>Published: Aug 16 / 11:44.</li>
<li>Views: 327, Clicks: 161, Score: 21.1</li>
<li>Via: <a href="/links/search.html?query=domain:timeago.yarp.com">timeago.yarp.com</a></li>
<li>Tags: <a href="/links/iphone?t=frameworks" target="_self" class="tags" rel="tag">frameworks</a>, <a href="/links/iphone?t=javascript" target="_self" class="tags" rel="tag">javascript</a></li>
<li>Submitter: <a class="user" href="/links/users/profile/111696.html">bloid</a></li>
</ul>
And the following to your CSS file to style our list:
.news-details
{
background-color:#fff;
color:#666;
margin:15px 0 0 10px;
padding:0;
font-size:12px;
line-height:150%;
list-style-type:none;
}
If you now go back to the story and reload it you should see something similar to the following:

Looking good! We still need to add our back button to this screen so, let's go ahead and do that now. After the closing DIV tag for story-body but before the final closing DIV add the following:
<p><a href="#stories" target="_self" class="previous">Back to Links</a></p>
No need to anything else, the style has already been added earlier when we added the previous link to our pagination navigation elements. Before I close of this article I want to point you to something. Click on the login link again and then click inside the text field to add your username and/or password. What is wrong here? Yip, the insert point is almost half way down the length of the text area and the 'background' text does not go away when clicking or even typing in this box. Let's solve these issues and while we are at it, let's tighten up the look and feel of both our dialogs.
Add the following styles for the fieldset and the H1 inside out fieldset:
fieldset
{
box-sizing:border-box;
width:100%;
margin:0;
border:none;
padding:10px 6px;
background-color:#7388a5;
}
fieldset > h1 {
margin: 0 10px 0 10px;
padding: 0;
font-size: 20px;
font-weight: bold;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0;
text-align: center;
}
To take care of our labels and input boxes we need to following CSS:
label
{
font-size: 14px;
text-align: left;
display: block;
margin: 5px 0 2px 4px;
font-weight: bold;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0;
}
label.inside
{
position: absolute;
margin: 14px 0 0 15px;
color: #BBB;
text-shadow: none;
}
input[type='text'], input[type='password']
{
width:95%;
font-size: 16px;
}
input
{
padding:5px 0 5px 3px;
box-sizing: border-box;
margin-right:20px;
font-size: 17px;
font-weight: normal;
}
At this point you can go ahead and refresh the browser and test out the login input boxes. Much better right? The background text is still there though, we will take care of that in a second. Let's refine the rest of the dialog elements. For the bottons and the select drop down add the following to your CSS file:
select
{
margin-right:20px;
width:90%;
font-size:16px;
}
input[type='submit']
{
float: right;
margin-top: 0;
margin-right: 20px;
width:100px;
}
Your dialog should no look a ton better and resemble the following screen: (remember if you are using an actual iPhone or the SDK on Mac, you may need to tweak some of the dimensions to get the same result)

Only one thing left, get rid of that annoying background text inside the username and password fields. For this we add some jQuery goodness. Create a new file inside the js folder and name it iphone.js. Add the following code snippet to this file and hit save:
$(document).ready(function(){
$('#username, #password').focus(function(){
$('#' + $(this).attr('id') + '-label').hide();
})
});Now in the head of the index.html page add the following to add the JavaScript file to our page:
<script type="application/x-javascript" src="js/iphone.js"></script>
If you do not have jQuery yet, then go ahead and download jQuery, save the file to the js folder and add the following to the head just before the iphone.js line:
<script type="application/x-javascript" src="js/jquery.js"></script>
Now go ahead and refresh the browser, launch the log in dialog and test out those form elements. What? It still does not go away? Not to worry there are two small things we have to change to our current set-up to get this to work. In the toolbar section of the index.html page change the link to the login form as follows:
<a class="login" href="#loginForm">Login</a>
Copy the code from the login.html file to the index.html just after the closing DIV of the story. Save everything and refresh the browser again. This time when you click inside the username or password fields the background text goes away. And that is it! That is how we got DZone on the iPhone. There are still some enhancements we would like to make and this article does not cover how the back-end was hooked up to this interface but I am sure most of you can figure that one out as there should not be to much of a difference, if any, then the way you are currently doing it with your desktop browser based apps. I hope you enjoyed this article, found it useful, learned something new and will start building your own versions for the iPhone using iUI. We encourage you to use the iPhone version of DZone at http://dzone.com/iphone and send us you feedback on how we can improve on what we already have and what you would
like to see in future versions.
- « first
- ‹ previous
- 1
- 2
- 3
- Login or register to post comments
- 9355 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









