Coding with Jesse

Making a 100% height interface

With interfaces on the web that resemble those of the desktop (and better), we often want to get rid of the ubiquitous scroll bar in our web applications. We want all the action to happen directly within the visible browser area. Instead of scrolling, we can offer users new ways to interact with the page like Ajax-based page flipping and sliding panels.

The basis of most scroll-less 100% height interfaces contain some CSS looking like this:

html, body {
    /* get rid of default spacing on the edges */
    margin: 0;
    padding: 0;

    /* get rid of that 2px window border in Internet Explorer 6 */
    border: 0;

    /* fill the height of the browser */
    height: 100%;

    /* no more scroll bar */
    overflow: hidden;
}

This resets the margins and padding of the page, gets rid of the chrome border in IE6, sets the height to 100% of the available space, and hides anything that goes outside the 100%. This needs to happen on both the html and body elements. Having a hidden overflow isn't mandatory, but it makes sure that nothing will cause that scroll bar to pop back up, even if you are dragging things off the edge of the screen.

Now, anything you put directly into the body can also be given height: 100% and it will do exactly what you expect (fill the page). This way, you can have the interface of the page grow and shrink along with the size of the browser. You can even have multiple layers fill the page like this:

/* css */
div.layer {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
}

<!-- html -->
<body>
    <div class="layer">
        .. some stuff ..
    </div>

    <div class="layer">
        .. some other stuff ..
    </div>
</body>

You can easily give an element within the page a scroll bar by throwing in overflow: auto on something that has height: 100% and some padding or margins.

We can also use JavaScript to calculate the size of the browser and use this to decide how much content we display at a time. We can do this with some relatively easy code, now that our body is the exact width and height of the browser "viewport":

function getPageSize() {
    return [
        document.body.offsetWidth,
        document.body.offsetHeight
    ];
}

Note that this code only really works with 100% height. On a page with a variable height, it's not reliable at all.

I've put this stuff to use with a lot of projects lately, most notably PictureSandbox (formerly FlickrCash).

For more details on calculating the width of the viewport, check out Viewport Properties at QuirksMode.

Published on June 2nd, 2007. © Jesse Skinner

Detecting and Debugging Timeouts and Intervals

When you start to cram a lot of JavaScript animations and Ajax onto a web page, it can become tricky to know all the code that's running in the background. When you start to detect some performance issues, it's equally tricky to track down what code is being executed.

Luckily, the only way to get code to run in the background with JavaScript is through the functions setTimeout and setInterval. And more luckily, we can overwrite these functions so that we can know whenever they are being called:

window.setInterval_old = window.setInterval;
window.setInterval = function(fn, time){
    console.log('interval', fn.toString(), time);

    return window.setInterval_old(function(){
        console.log('interval executed', fn.toString());
        fn();
    }, time);
};

window.setTimeout_old = window.setTimeout;
window.setTimeout = function(fn, time){
    console.log('timeout', fn.toString(), time);

    return window.setTimeout_old(function(){
        console.log('timeout executed', fn.toString());
        fn();
    }, time);
};

This will send output to Firebug whenever a timeout or interval is first initiated, and again when the function is actually called.

This solution is rather nice because it will let you know what 3rd party JavaScript widgets are doing in the background as well without needing to add debugging messages to them. You could overwrite nearly any method like this to get similar debugging messages as well (like document.getElementById or Array.prototype.push or practically anything).

Published on May 28th, 2007. © Jesse Skinner

Wikipedia Discussions

I'm sure you all use Wikipedia on a regular basis. But what you may not be aware of are the millions of hours of hilarity, entertainment, and often revealing information waiting for you on every page of Wikipedia. At the top of the page is a link to the "Discussion" of that page, where the authors talk about what they want to add or remove, and often get into great, long debates (read: flamewars) about the contents of that page.

Some notable examples I've uncovered:

Just a small preview of the 1,797,673 "Talk" pages waiting to be discovered. If you find any other gems, please let us all know in the comments!

Published on May 23rd, 2007. © Jesse Skinner

Redirecting after POST

When working with forms, we have to think about what will happen when someone clicks back or forward or refresh. For example, if you submit a form and right afterwards refresh the page, the browser will ask if you want to resend the data (usually in a pretty long alert box talking about making purchases).

People don't always read alert boxes, and often get used to clicking OK all the time (I know I fall in this category), so sometimes comments and other things get submitted more than once.

To solve this, you can simply do an HTTP redirect after processing the POST data. This is possible with any server-side language, but in PHP it would look something like this:

if (count($_POST)) {
    // process the POST data
    add_comment($_POST);

    // redirect to the same page without the POST data
    header("Location: ".$_SERVER['PHP_SELF']);
    die;
}

This example assumes that you process the form data on the same page that you actually want to go to after submitting. You could just as easily redirect to a second landing page.

On this site, on each blog post page, I have a form that submits to the same blog post page. After processing the comment, I send a redirect again to the same page. If you add a comment and then refresh, or click back and then forward, the comment won't be submitted twice. (However, if you click back and then click Add Comment again, it will. I really should filter out duplicates, but that's another topic.)

This works because you essentially replace a POST request with a GET request. Your browser knows that POST requests are not supposed to be cached, and that you should be warned before repeating a POST request. After the redirect, the page is the result of a simple GET request. Refreshing the page simply reloads the GET request, leaving the POST request lost between the pages in your browser history.

Published on May 22nd, 2007. © Jesse Skinner

Helping visitors with .htaccess

When I changed all my URLs, I put in place something to email me whenever there was a 404 (page not found). This way, if I screwed up something with my forwarding, I'd know.

It turned out that people were getting 404s mostly for one of two mistakes. Either there was spaces in the URL (an error of copying and pasting perhaps?), or there was a trailing period at the end of the URL (probably from it being part of a sentence, and the period becoming part of the URL when auto-linked).

The two broken URLs look like this:

http://www.thefutureoftheweb.com/blog/helping-visitors-with-htac cess

http://www.thefutureoftheweb.com/blog/helping-visitors-with-htaccess.

I thought I'd make things easier for people by auto-correcting these two mistakes. I added a few lines to my .htaccess file like so:

RewriteEngine on

# remove spaces in URL as a favour to visitors
RewriteCond %{REQUEST_URI} .+\s+.+
RewriteRule ^(.+)\s+(.+)$ /$1$2 [L,R=301]

# remove trailing periods on URL as a favour to visitors
RewriteCond %{REQUEST_URI} \.+$
RewriteRule ^(.*)\.+$ /$1 [L,R=301]

Unlike my major changes the other day, I'm not obliged to maintain these rewrites forever — after all, they are still mistakes in the URL, and I'm not giving out URLs with spaces and dots in them on purpose. However, I'd rather bring people to the correct page if I can.

Published on May 21st, 2007. © Jesse Skinner
<< older posts newer posts >> All posts