Making Sign-Up Forms That Much Easier

Come in we’re open. But first you must fill out a form.

I watched a useful presentation about form design by Ryan Singer a while back and he makes a point that user input should be kept to the bare minimum when signing up. An article by Luke Wroblewski titled Sign Up Forms Must Die seconds the notion.

However, sometimes it is required for the user to do the leg-work and fill in lots information. Like registering at an online store for instance. Gotta at least know where to send the purchased goods! For users that don’t have auto-fill plugins such as the Google Toolbar, Javascript can help filling in the necessary information - not just validating the user input. It would, for instance, make web forms simpler for users if their city would be filled in automatically upon entering their postcode. I timed myself typing my city of residence as quickly as possible and it took me about 3 seconds. So, all in theory of course, the improvement would skim roughly that much time off filling in a form. it’s not a lot, but the users level of tolerance towards HTML forms isn’t much either.

I’m now going to talk about how I “auto-fill” the city information based on the entered postcode in a HTML form using PHP, MySql and Javascript.

Setting Up the Database

First I needed to get the postcode information from somewhere. For Switzerland I found the data via the Swiss Post.

Then I needed to get it into the database somehow. To do this I imported the downloaded text file [it is tab delimited] into Excel and then exported it again as a CSV file. I only imported the columns I needed which are the ones containing the Town name and the postcode.

Next I put on my white programming gloves and wrote a quick PHP script that connects to the database and runs two SQL statements to import the CSV file. Change the variables to fit your setup and be sure to have the CSV file in the same folder when you run the following example.

$link = mysql_connect('localhost', 'root', 'root') or die('Could not connect: ' . mysql_error());

mysql_query("CREATE DATABASE IF NOT EXISTS test
DEFAULT CHARACTER SET latin1 
COLLATE latin1_swedish_ci;"
);

mysql_select_db('test') or die('Cannot select database');

mysql_query( "CREATE TABLE postcodes (
code SMALLINT(4) NOT NULL ,
city VARCHAR(27) NOT NULL ,
PRIMARY KEY (code)
) ENGINE = MYISAM CHARACTER SET latin1 COLLATE latin1_swedish_ci"
);

mysql_query( "LOAD DATA LOCAL INFILE 'postcodes.csv'
INTO TABLE postcodes
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\r\n'
(code, city);"
);

if(mysql_error()) echo mysql_error();
else echo "Done!";

mysql_close($link);

Special characters where also an issue with importing and exporting so be sure to check the city names in the database. I used the windows text format for importing.

Creating a Web Service

We now need some server side code to provide us with the data from the database. I am using the insanely cool JSON gateway included with AMFPHP to provide the client with data. So with AMFPHP set up I could just write a simple PosteCodeFinder class and plop it into the AMFPHP's services folder. Otherwise a simple way to get the correct name would be a PHP script like this:

mysql_connect('localhost', 'root', 'root') or die('Could not connect: ' . mysql_error());
mysql_select_db('zencart_shop') or die('Cannot select database');

if (isset($_GET['code']) && is_numeric($_GET['code'])) $code= $_GET['code'];
else die ('Only numbers please! The URL should look something like: postcodefinder.php?code=1000');

$query = "SELECT city FROM postcodes WHERE code =" . $code . " LIMIT 0 , 1 ";

header("Content-Type: text/javascript; charset=utf-8");
echo json_encode(utf8_encode(mysql_result(mysql_query($query), "city")));

The code connects to the database, checks the input to be numeric, retrieves the city name and spits it out as JSON.

I had to use utf8_encode() inside the json_encode() function because it would otherwise mess up my character encoding. "Genéve", for instance, would be truncated to "Gen". It was reported as PHP bug #43941.

The Client-Side Code

On to the Javascript! I am using the Prototype library here to make programming AJAX bearable.

FormController = {
    previousResponse: "",
    
    postcodeHandler:function(el) {
        var code = parseInt(el.value);
        if (code >= 1000 && code <= 9999 && $F('country') == 204) 
          new Ajax.Request(
            "json.php/PostCodeFinder.find/" + code, 
            {
              method: "get",
              onSuccess: FormController.updatePostCode.bindAsEventListener(this)
              }
        );
    },
    
    updatePostCode:function(transport) {
        var jsonObj = eval("(" + transport.responseText + ")");
        if (jsonObj != undefined && ($F('city') == "" || $F('city') == previousResponse)) {
           $('city').value = jsonObj;
            this.previousResponse = jsonObj;
       }
    }
}

Event.observe(
    window,
    'load',
    function() {
        new Form.Element.Observer('postcode', 1, FormController.postcodeHandler.bindAsEventListener(this));
    }
)

Download all the code here.

Conclusions

Usability Gain

Considering the amount of time it took me to implement, I can only recommend the postcode optimization as a final improvement to a form. Other factors, as mentioned in Singer's video for instance, should be considered first to get the most bang for the buck.

Character Encoding

Most of my time was not spent with programming the scripts but with playing with the character encoding. Having UTF-8 all the way through form the original text file, to the database, to the PHP script, to the client side Javascript and finally the HTML document itself was easier said than done. I found a PDF on the UTF-8 development practices by Matthias Kerstener, that helped out with some of the issues. Still, character encoding issues makes me not want to be a web developer any more.