Graceful degradation: Drupal and JQuery

Graceful degredation is essential if you're creating web content that may not render reliably in all browsers. Here's some ways to play with Drupal and JQuery - replacing flash animations with back-end-editable, SEO-friendly html and serving up cross-browser page transitions - while also providing reliable fallbacks when things go wrong.

When a client requested a flash-based, CMS-backed website that also scored high on SEO and could be viewed usefully by non-flash users, it got me to exploring JQuery animation as a flash replacement.

Admittedly, there's nothing radical about that, but it did allow me to stack the whole site on top of Drupal - making for easy editing, secure and solid foundations, easy expansion, and great SEO potential... all without ditching the animation they were after.

The problem was how to build the site to both animate and not animate - ie: how to work within the confines of a cms to make animations that work reliably but also degrade gracefully in the absence of JQuery kicking in.

You can view the finshed site at www.baburrestaurant.co.uk and get a feel for what was built. I'm writing up these notes in case it helps anyone else looking to do the same kind of tricks.

Animating page loads

To take a step back, the agreed plan for the build was to put a slideshow on the front page, but also to provide non-JQuery content in case javascript failed or wasn't available in the browser.

The client was also adamant that we should have animated page transitions throughout the inner pages. And since the client expected to be adding and moving pages over time, any page transitions we put in place had to work by default with all pages that require them - both existing and future.

The whole build came down to a few tricks...

The first was to define templates with content regions that had start and end animation states, then assume the end animation states were what we'd show by default to non-JQuery viewers. JQuery capable viewers would get to see this also, but with the bonus of seeing these regions animate into position on page load, then animate out on exit.

Tackling the two cases is pretty simple. It's easy to load an initial non-JQuery stylesheet that renders the site with the end-states for the default views. It's then easy to conditionally load a stylesheet for JQuery viewers just ahead of doing any animations, and set the start points for elements ahead of the page-load eye candy.

With JQuery, users get animation. With no JQuery, users just get the static view and everything degrades gracefully.

Simple strategy. Here's the code that does it:


<link rel="stylesheet" type="text/css" href="coreStyles.css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
    if (jQuery) {
        // IF JQUERY IS LOADED, load JQuery stylesheet with start positions for elements here 
        document.write('<link rel="stylesheet" href="jquery-styles.css" type="text/css">');
        $(window).load(function () {
        // put any JQuery animations here so they animate on completion of page load
    });
}
</script>

All you need to do is stick with a few templates, define any animated regions with easy to target CSS IDs, then let your JQuery and conditionally loaded stylesheet do the rest.

Making the page-load animations is a snap. The next problem is animating the page exits.

 

Animating page exits

If the pages don't animate out smoothly, there's an abrupt visual disconnect when the next load animation starts. You need easy out, easy in for this to work smoothly.

There's always the possibility of a stall while pages load, but if you can get the elements to successfully and smoothly exit stage right on page exit, your brain glosses over the pauses that happen while the next page gets set to enter page left.

While its possible to trigger events on page exit, the problem is that browsers handle this differently. The chief problem with this approach is that you can trigger events with a page-exit event, but it isn't possible to reliably stall page load while the exit animation runs its full course. The user can click on a link to the next page, the exit animation will start, but the next page can load before the exit animation completes.

JQuery, fortunately, provides a neat way of overriding link behaviour through event.preventDefault(). For key links (such as menu navigation) its simple to override links to trigger an function that rolls though the page exit and then gracefully loads the next page on completion.

Here's the code for that part:


// this code triggers a page exit animation for any link in the #menu div, or any link with class='transition'
$("#menu a, a.transition").click(function (event) { 
    event.preventDefault(); 
    // place your exit animation here
    destinationUrl=$(this).attr("href");
    // load the next page
    delayedPageExit=setTimeout("window.location.href=destinationUrl",2000);
});

Yay JQuery.

 

Animating the front page

The last trick is to swap the front page content around for JQuery or non-JQuery users... glossy animations for JQuery kids; static content for others.

Since we already have stylesheets for both cases, its easy to set display:none for any JQuery content in the non-JQuery stylesheet. This allows you to hide JQuery dependent content (like slideshows) by default - taking care of the non JQuery users.

The same trick can be used in reverse in the JQuery stylesheet to hide the (default) non-JQuery content and re-enable display of any JQuery-based content.

All thats left to do is duplicate the content on the necessary pages to display both sets of content then let the stylesheets that load determine what's visible and what's hidden (while avoiding duplicating too much content verbatim between the two to avoid a hit on SEO).

Luckily in Drupal, its easy to define blocks and views to generate and display content on specified pages. This allows you to easily create two ID-tagged blocks (one JQuery, one non-JQuery) that both show on the any page you specify.

For a front-page slideshow, first define a drupal content type of 'slideshowItem' with an imagecache-generated teaser image (so that clients don't need to prep their images to size) and add a rank field that you can sort on to set the running order. Add as many slide items as you like. Its then simple to create a view that dumps their teasers out in order and shows them in a block.

You can then assign this block to show only on the front page ('<front>'). That's your slideshow content done.

Now create a static HTML block for the default (non-animated content)... also shown only on '<front>'. Stack the blocks one on the other in the same template region. That gets you both sets of content in the same place on the front page.

Now use the JQuery cycle plugin to target the block or div that contains the slides and you have a slideshow that - thanks to the css tricks above - only shows to the JQuery capable users.

Load the cycle plugin:


<script type="text/javascript" src="jquery.cycle.all.js"></script>

Place this in the JQuery animation block that you run on page load:

$("#whateverContainsYourSlides").cycle({fx:'fade',speed:3000,timeout:6000});

Slideshow done and running.

 

Creating custom slide formats

For better styling, install the drupal contemplate module to style teasers that appear as slides just as you want them. If you need captions or custom slide formats this is a great way to do make a slideshow that fits your requirements for slide structure. You can animate or structure pretty much any slide content you want this way - with captions, custom fields, clickable thumbnails, or whatever else you need.

As a final tip, if you find that blocks generate a whole heap of html frame that you don't want to keep CSS-ing out, just create a block.tpl file in your theme folder that contains only the following and so drops the unwanted junk. It'll override the default block.tpl file. If your theme already has a block.tpl file, keep a copy of the old one just in case.

<?php print $block->content; ?>

 

 

So thats JQuery page loads, page exits, front page presentations, and graceful degradation, done with Drupal. If you're after a strategy for tackling it, hope the above helps. If you want to correct my code, feel free. All suggestions welcome.

Article posted Wednesday 5th May 2010

Search