Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / CSS

Step by Step Guide to Building a Cross-Platform Application in HTML, CSS & JavaScript

0.00/5 (No votes)
20 Jan 2012CPOL13 min read 35.3K  
This is a step by step guide to building a cross-platform application in HTML, CSS and JavaScript

Back in the days when your computers came in options of the cream, the white, the off-white, the ivory or the beige, it was very frustrating that an application you put so much effort into wasn’t usable on other computers.

You had to make a choice, and my choice was Windows. It was just when Microsoft .NET came out and we figured it was a pretty good bet, which it was I reckon.

Then Came the Web…

A year or so later, despite many of our customers still being in dial-up, I moved to the web. Unfortunately, this came with almost more headaches – Internet Explorer 6 was the king of browsers, but we had a few Netscapes and this rogue called Firefox was starting to make waves.

Over the last 10 years, while I moved completely away from desktop applications, the browser wars just got worse – even Internet Explorer couldn’t get versions working nicely together (compatibility mode??? WTF?).

Finally, although Internet Explorer is still not perfect, it is definitely better and more importantly – you can pretty much ignore it and code just for ‘standards compliant’ browsers – Firefox and Chrome predominantly.

And then the iPhone came along.

…then came the iPhone…

Actually, the iPhone web browser is incredible – I’m more confident of my websites running on the iPhone than I am in Internet Explorer. And on top of this, they have to cram your site into a tiny little screen. I’m very impressed.

Of course, the iPhone really comes into itself with its applications (as opposed to websites). Not only do they run beautifully, but the entire App Store infrastructure exposes a developer’s work to millions of ready and willing credit card holders, eager to part with a couple of dollars just for the pleasure of the App Store buying experience.

Unfortunately, the iPhone also forced you to code in yet another language – Objective C – and I’m sorry, but I can barely keep up with .NET, let alone learn another language built on C of all things. I guess I wasn’t the only one to mourn this because there came a slew of WYSIWYGs and cross-platform compilers (Moonlight anybody?). And to the top rose PhoneGap – a platform that essentially ‘wraps’ a web application in Objective C to convert it into a regular iOS application. Not only that, it will also wrap it in the relevant languages to support Android, BlackBerry, Windows Phone, Symbian, etc… Such a simple concept, but just amazing.

…and the desktop comes full circle…

Windows 8 purportedly supports native HTML/JavaScript/CSS applications.

Woah – so suddenly, my old-school web-coding skills can be deployed on the web, major mobile devices and 90% of the world’s desktops? Amazingly, yeah – I think they can.

The HTML/CSS/JavaScript Application

So, sorry for the long pre-amble – I just need noobs to appreciate that this next decade of development shouldn’t be taken for granted.

The point is that now you can build an application in ONE language and deploy to multiple platforms. However, it’s not quite as easy as that – there are many restrictions to what can be built and how. In this article, I’m going to walk you right through from start to finish. I’ve built a few of these applications by now (the most recent is www.stringsof.me), so I’ll point out the pitfalls and hopefully save you a bit of time.

Know the Goal

I should point out that it is FAR easier to build an application with the knowledge of its intended use. If it’s going to be used on iPhone or wrapped in PhoneGap, then you can test it incrementally on these platforms as you go. Far far easier than trying to retro-fit an existing web application. In fact, I recommend to anybody that no matter how big your web application is, you just start from scratch. Copy/paste what you need from the old one, but start with a clean slate – after all, this app will be used for years and years so you better make it a nice one. So, here’s our goal:

HTML-Architecture-Overview

 

The Application

The application consists of three parts – an HTML file, one or more JavaScript files and one or more CSS files.

Below, I’ve created a completely stripped-down application to try to indicate the core functionality, however if you want to see the full-blown thing in action, I suggest you View Source on m.stringsof.me.

Index.html

HTML
<html>
<script src="jquery.js" type="text/javascript"></script>
<script src="phonegap.js" type="text/javascript"></script>
<script src="settings.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>

<link href="style.css" rel="stylesheet" type="text/css" />
<link href="settings.css" rel="stylesheet" type="text/css" />
<body>
<div id="MyContainer" >
    Hi everybody, welcome to my App.
</div>
<script>
    var app = new App(‘MyContainer’);
    app.Start();
</script>
</body>
</html>

Nothing particularly flash here, but of note:

  • We are using jQuery, but that is just my preference
  • The PhoneGap.js file is required for our various AppStore installations, but on the web server, we replace with just a stub file
  • The Settings.js and Settings.css files enable us to manage the minor variations between our various platforms. For example, iOS requires you to ask people before sending them push messages, Android doesn’t care, and push messages are irrelevant on a web-based app

     

    Settings.js

    The Settings file contains platform-specific variables.

    JavaScript
    var Settings = function(){
        return {
            SiteRoot:'http://api.stringsof.me/’,
            ConfirmPushNotificationsOnStartup: false
        }
    }();

    Data.js

    Data provides connectivity to our server. This class could be stored in the main App.js file, but I’ve split it out here because in a bigger application, you’d have lots of JavaScript files and you don’t want circular references if you can help it (not that JavaScript minds, grrrrr).

JavaScript
var Data = function () {
    var that = this;
    this.SiteRoot = Settings.SiteRoot;
    return {
        CallJSON : function(route, params, callback) {
            var triggerName = new Date().getTime().toString();
            $("body").bind(triggerName, function(e, result) {
                callback(result);
            }); 

            // Add the JSON callback to the parameters
            params._JsonPCb = 'Data.OnCallJSON‘;
            params._JsonPContext = "'" + triggerName + "'";

            // Make the JSON call
            $.ajax({
                url: that.SiteRoot + route,
                data: params,
                type: 'GET‘,
                dataType:"jsonp"
            });
        },

        OnCallJSON : function(result, triggerName) {
            $("body").trigger(triggerName, result);
            $("body").unbind(triggerName); }
    };
} (); 

App.js

Encapsulates our main application code. This file will usually get pretty big, but you can split it up later depending on your coding style.

JavaScript
var App = function(containerID){
    this.ContainerID = containerID;
    var that = this;
    this.Start = function(){
        var $con = $(‘#’ + that.ContainerID);

        // Get user
        Data.CallJSON(‘Person/GetPerson’, {userName: ‘ben’}, function(person){
            $con.html(‘Welcome ‘ + person.FirstName);
        });

    }
    return {
        Start: that.Start
    }
};

Pretty simple. All it does is get the user by their username (I have hard-coded ‘ben’ in this example). On the callback, you display their name in our main <div/> element.

One flashy thing I’ve done is use my method for calling JSONP with callbacks. You can read up on it here, or take my word for it that it works.

Data Access

Most of us are used to dealing with server-side languages such as ASP.NET or PHP. Despite some efforts (MVC perhaps), these technologies still leave the HTML dependent, and to a certain extent aware, of the code that generated them. For example, ASP.NET is heavily dependent on ViewState. This may be fine for your web application – even your mobile web application – but it is worthless in your iPhone or Android app.

Without a server-side language to create and bind our HTML, we must defer to JavaScript. And to get the data required to bind, we must make some kind of web service call. Because we are using JavaScript, it makes sense to return JSON-formatted objects.

Cross-Domain Data Access Using JSONP

Using JSON would be all you had to do (and in fact frameworks like ASP.NET MVC have excellent JSON support baked in), except that we want to use the same web service (and therefore the same returned objects) in our iPhone/Android application. Consider our mobile web application:

Essentially therefore, our application is getting JSON objects from the same domain (m.stringsof.me) as where it resides. Now consider our iPhone/Android application:

  • our web service (written in ASP.NET for example) is at http://m.stringsof.me/service
  • our application is stored on the phone – it doesn’t have a concept of ‘domain’

It doesn’t have a domain, which means we are doing something called cross-site scripting. Unfortunately, despite many many modern web applications using it (check out the source of the Google home page), all web browsers consider this a gross security risk and your JavaScript will error if you try to do it. Enter JSONP…

JSONP (JSON with Padding) gets around this issue with a crafty little trick which I’ve covered in another article. You need to know how JSONP works in order to build your mobile application, so I suggest you brush up.

Returning JSONP from an ASP.Net MVC Application

As an aside, if you are an ASP.NET MVC user, you can create your own JsonpResult:ActionResult to return from your Views:

JavaScript
public class JsonPResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;
        var request = context.HttpContext.Request;

        // Open the JSONP javascript function
        var jsonpCallback = request.Params["_jsonpcb"];
        response.Write(jsonpCallback + "(");

        // Defer to base class for rendering the javascript object. Because we
        // have opened a javascript function first, it gets rendered as the first parameter
        base.ExecuteResult(context);

        // Add any additional parameters - this is not part of JSONP, but
        // a construct I've written to allow me to pass extra 'context' to the server and back
        var extraParams = request.Params["_jsonpcontext"];
        if (!string.IsNullOrEmpty(extraParams)) response.Write(extraParams);

        // Close the JSONP function
        response.Write(");");
    }
}

Using this, you can return JSONP directly from your regular MVC Controllers:

C#
public class PersonController : Controller
{
    public ActionResult GetPerson(string username) {
        var person = new DataService().GetPerson(username);
        var result = new JsonPResult {Data = person};
        return result;
    }
}

Awesome huh?

Rendering Your Data

Because I have returned JSON from the server, the next step is to render it to HTML. Without going into too much detail, I personally use a couple of methods:

  • HTML templating. I store HTML in a separate file (or hidden DIV in the Index.html page) and then bind sections of it using jQuery
  • use jQuery to create HTML such as $(‘body’).append($(‘<div></div>’).html(‘Hi there’));

Why Not Just Return HTML from the Server?

Good question, glad I thought of it. Technically, there’s no reason why you shouldn’t. In fact, if you did, you wouldn’t need to jump through all those cross-domain hoops with JSON, etc.

Frameworks like ASP.NET MVC actually encourage you to do this with their ‘View’ system and when I built the www version of stringsof.me (www.stringsof.me), I used these and they worked great.

When I moved to a mobile version of the application however, I found that this was a little short sighted. What if I expose my objects to a third party who wants to use them in, for example, a Facebook plug in? The HTML I return for ‘GetPerson()’ is not likely to suit their purposes so best to return the object and let them format it themselves. Or what if the returned HTML expects JavaScript to scroll it into place, but the requesting device doesn’t support JavaScript?

Although it is easier to specifically write your HTML (and even binding if you are using MVC), I eventually concluded that plain JSON objects are the most versatile mechanism. By using JavaScript as the rendering agent, you can make decisions based on the state of the client (such as width or support for location-based queries) which aren’t necessarily available to a server-rendered page.

Media Queries

Now that you are getting your data, the next step is adjusting the presentation between the various platforms. For example, a full-blown website may have a big background image and the mobile version may remove this to account for low-bandwidth phones visiting it. The fashionable way to do this these days is via CSS Media Queries.

The basic premise is that your CSS reacts according to the type of media that is using the device. For example:

CSS
body{
    background-image:url(‘bg.jpg’);
}
@media screen and (max-device-width : 320px){
    body{
        background-image:none;
    }
}

The code above says:

  • for the body of the page, use a background image of bg.jpg
  • however, if the screen is smaller than 320px, do not show a background image

The beauty of this is that it is platform independent – you don’t have to detect an iPhone or Android, you just have to know what its screen resolution is. You can also switch out based on orientation (if the device is held upright or on its side):

CSS
@media screen and (orientation: portrait){
    body{
        background-image:url('bg_narrow.jpg');
    }
}
@media screen and (orientation: landscape){
    body{
        background-image:url('bg_wide.jpg');
    }
}

Now, if the user turns their iPhone on its side, the background image will switch out to one that better suits its new dimensions. Snazzy huh?

Testing Your Code

Now that you have your HTML, JavaScript and CSS working, you need to test it. I have found that the best mechanism is Firefox using the Firebug plug in. This will get you 99% of the way there, even for your iOS/Android applications later.

You can test your media queries simply by resizing your browser window to the appropriate dimensions – getting a pretty decent idea of how your site will look on an iPhone compared to a full-size desktop browser. Check it out by opening m.stringsof.me in a new browser window now and resizing.

Deploying Your Application as a Regular Website

This is the easy one you’re probably used to – just create the website on IIS/Apache or whatever you are using and copy the HTML/JavaScript/CSS files over. The first time you deploy, remember to create Settings.css and Settings.js files and set them appropriately – they shouldn’t be in the main solution because they differ for other deployments.

Remember you’ll also need to create an empty (or stubbed) PhoneGap.js file, as per the file reference in your Index.html file. If you don’t, your site will still run but your visitors will get an unprofessional ‘404 Page Not Found’ error.

Deploying Your Application as a Mobile Website

If you’ve used your CSS media queries correctly, your main website will double as your mobile website – no changes are required.

Deploying Your Application as an iPhone and/or iPad App

Ah, the part you’ve probably been waiting for all along.

  • Download www.phonegap.com and follow their instructions for creating a new project in xCode on your Mac (sorry, you need a Mac computer to build an iPhone app)
  • PhoneGap has comprehensive help files, so you are best off following them, but essentially the next step is to copy your HTML/JavaScript/CSS files into the ‘www’ folder that PhoneGap provides. Again, create new Settings.js and Settings.css files accordingly.
  • Note that PhoneGap also includes a PhoneGap.*.js file which contains the JavaScript wrapper code to access the device hardware such as the camera. Make sure the file is named exactly the same as that referenced in your Index.html file.

Compiling and building your PhoneGap application is beyond the scope of this article sorry.

Beginner’s Tips

  • iOS is case-sensitive, so the <link/> and <script/> file references in your Index.html file must match the case of the files themselves. If your CSS refers to images or folders, these are also case-sensitive. This took me about two hours to figure out – too much PC for me I guess.
  • I use DropBox to synchronize changes between my PhoneGap application and my website application. Even though you are using the same HTML/JavaScript/CSS files, they are copies of each other so a change in one must be copied to the other. If you’re a PC user, you may also like to use File Backup Pro to quickly prepare and copy your changes to your server.

Deploying Your Application to Android (and Other Mobile Devices)

This uses PhoneGap again, and is the same as the iPhone installation above. Again, the PhoneGap documentation does a much better job of explaining this than I can.

Limitations

The solution I have presented involves building a website predominantly in JavaScript, and I have a few problems with this:

  • There is no compile-time checking. I have colleagues that rave about Script#, but I don’t like the additional learning curve.
  • Mainly because of point 1 above, it is hard to enforce architecture or development styles. This makes working in a multi-developer team environment quite a bit tougher.
  • Search engines do not execute JavaScript which means all they see on your web page is a little bit of HTML wrapper. This means your website will not rank in Google, Yahoo, etc. You must therefore invest more in other SEO methods such as Site Maps and friendly URLs.
  • JSONP can only be executed using GET requests, so if you need to upload a huge amount of data, such as an image, you are out of luck. In m.stringsof.me, I had to deal with this when uploading the image that the user draws on the <canvas/> element. I eventually solved it by breaking the base64-encoded representation of the image into 1000kb chunks and sending them to the server one after the other. The server remembers what it gets and joins them together into a proper image at the end. (This is why you see a percentage completion status when saving your work – each increment is a chunk of image). You can View Source on the page to see how it was done, if you like.

Summary

Everybody likes a summary section so they know it’s the end of the article. So, there you go.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)