Tech Times #310 Website Tips, Tricks, News and Reviews for Web Coders

Just reposting my newsletter from SitePoint Tech Times and see how it renders in Posterous.

SitePoint Tech Times

Issue 310: September 28, 2010 Tips, Tricks, News and Reviews for Web Coders Introduction It's Tech Times time once again! This week we have more techy tips and tricks than you can handle. Seriously, I dare you. First up, James Edwards shares a technique for solving the ever-present issue of accessibility in drop-down menus -- once and for all. Then, Craig Buckler will show you how to implement a bulletproof fallback for your fancy new canvas animations. And finally, we have a treat for you Ruby-heads out there (like me). Craig Anderson walks through building a dead-simple URL-shortening service using Sinatra and Heroku. Fire up your code editor and brew up a fresh pot of coffee. It all starts right after the jump.
_______________________________________

Summary
• Introduction
• The Footer's the Menu
• Cross-Browser HTML5 Canvas with Fallback
• A Ten-minute URL Shortener
• New Technical Articles
• Techy Forum Threads

The Footer's the Menu Drop-down and fly-out menus often come with a whole range of usability and accessibility problems -- skittish behavior, content that disappears below the fold, or lack of keyboard access, to name but a few. For the most part, though, these problems are solvable (see: "The Right Way to Make a Drop-down Menu" for more). However, there's another issue that's trickier to address: the question of how and where to create the content these menus use. There are basically two options: • Create the menus as hierarchical lists in hard-coded HTML. The top navigation bar is an unordered list, and each of its submenus is a nested list, a child of the top-level item that triggered it. The problem with this approach is that it means creating a large structure in HTML, which can be quite annoying for screen readers and other serial devices to navigate their way through. Put simply, it's a whole lot of content to put in static HTML, when some user agents lack the ability to selectively show and hide it. • Generate the menus on the fly. The submenu content is created as required, from configuration data in JavaScript, and appended or removed from the top-level triggers as necessary. The problem with this approach is that the content is then inaccessible without scripting, or in browsers that don't support or fire the triggering events. This solution relies on other forms of navigation (such as a sitemap) to make up for the shortfall in content. Neither of these solutions are perfect; one group suffers reduced usability, or another user group suffers reduced accessibility. If only there were a third way ... The Third Way The image below is an abridged screenshot from a sample web page that's typical of many site designs, in that it has both a header and a footer: The header and footer of a sample web page The header contains the primary navigation from which the drop-down menu would be triggered, and the footer contains supplementary navigation. Notice anything similar about them? Each link in the top navigation bar is to one of the main sections of the site. And each set of links in the footer is for the same main sections of the site. They correspond exactly, and when it comes to implementing the drop-down menu, it's likely that it will contain the same links we see in the footer. In other words, the footer is the menu. So why not literally make it the menu? Send in the Clones Let's design a menu with a single list of links as the top navigation bar; then, when a mouse or keyboard event triggers the appearance of a drop-down menu, we'll create that menu by cloning the corresponding section of footer links. This is the best of both worlds: • We do have the menu content in static HTML, but it's structured, usable, and out of the way. The usability issue with having all the content there by default is solved. • We are generating the menus on the fly, but we're doing it using content that nevertheless exists in static HTML. The accessibility issue with using dynamically created menus is solved. I've made a fully functional demo to illustrate this concept, adapting a menu script from the drop-down menu post I linked to earlier. The menu script is imperfect; if I had more time I'd tighten up the event flow (and test it in IE6!) -- but it should serve to demonstrate the concept nicely, and it's a good starting point if you wish to develop this idea further. Making Mega Menus And we can take this concept a whole lot further! Recently I did some work for Amnesty International Australia, where the site design called for complex mega menus containing large amounts of content: A mega menu from the Amnesty International Australia website The site also featured a footer with structured links in sections, very similar to the first demo I showed you. The footer from the same site So I was able to use this technique to implement the basic menu. But I could take it much further, by adding supplementary content to the footer links hidden in the footer. The thumbnails and long descriptions of each menu links is still present in the footer, but you can only the main links themselves. The difference is just CSS -- when the lists of links are in the footer, the additional content is invisible; when they're in the mega menu, it's made visible and restyled. Visit the Amnesty International Australia website to see this in action. You can also download a demo zip file if you'd like to play with the idea some more. It's an idea that could be extended in many ways; in theory, any kind of dynamic content could be designed in this way -- sourcing its data from another part of the page, where it exists in a different form. You can leave your comments on the blog entry: The Footer's the Menu
by James Edwards
________________________________________

Cross-Browser HTML5 Canvas with Fallback Canvas is a relatively new HTML5 technology that provides a "scriptable" image. You can add a element to your page and draw on its surface using JavaScript commands. It's very fast and could be used for action games and animated charts -- see "3 Great JavaScript & Canvas Examples." A canvas tag can be placed on an HTML5 page with the following code:

Sorry, your browser cannot display this image.

Assuming canvas is supported, JavaScript can be used to draw directly onto the canvas; for example, a black circle with 100px radius in the center of the image: var canvas = document.getElementById("mycanvas");
var cxt = canvas.getContext("2d");
cxt.arc(150,150,100,0,Math.PI*2,true);
cxt.fill();

That's great, but it's hardly a pleasant experience for people using a browser without support. There are several projects that implement canvas support in Internet Explorer 8.0 and below, but they cannot fix other browsers. We can fall back to text (as shown) or an img, but that requires extra markup, and it won't appear if the browser supports canvas but has JavaScript disabled. In that situation, the user sees an empty box. In order to keep everyone happy, we'll build a page that shows a simple canvas raindrop animation. If you're in the UK, it'll remind you of a glorious British summer. A static image will appear when the user's browser is without or JavaScript support. Working through the code, our HTML5 head contains a small script that declares a canvas element. This adds nothing to the page; it's a workaround for IE8 and below that allows us to apply CSS styles to a canvas tag, even though the browser is not HTML5-aware (alternatively, we could have used the HTML5 JavaScript shiv):
We can now define CSS styles for our canvas tag. When the page loads, the element is given a background image (rain.jpg) that everyone can see. The #mycanvas.active declaration removes this background, but it will only activate once our script runs successfully: We can now place a canvas tag on the page. There's no need to provide fallback text, as users will see the static background image when it's unsupported:

HTML Canvas Example with Image Fall Back

We're now ready to add some JavaScript magic -- assuming the user has scripting enabled. The first few lines check whether canvas is supported, and applies a class of "active" to the element to remove the static background:

This is a simple demonstration, but it illustrates how you can use new HTML5 technologies while retaining a level of support for older browsers. The same concept could be used in other applications; for example, you could provide an animated chart that falls back to a static, server-generated image when canvas or JavaScript is unavailable. Leave your comments on the blog entry: Cross-Browser HTML5 Canvas with Fallback
by Craig Buckler
________________________________________

A Ten-minute URL Shortener Downstairs from SitePoint, the team at 99designs recently revamped its URL shortener to support a bunch of new features. Using a combination of Sinatra and Heroku, I was flabbergasted at how easy it was. What We're Building Before I jump in, let me make it clear: we're not building a full-blown URL shortener like bit.ly or TinyURL. Instead, we're going to take a request like http:// gentle-light-20 .heroku .com/khw (Heroku will give you your own randomly generated, serene-sounding name for development purposes) and redirect the user to another URL. In this example, we'll send the user to http:// blogs .sitepoint .com/?p=26564, which WordPress will redirect to a friendlier URL. I've also assumed you have the following installed on your system: • Ruby • the Gem package management system • the RSpec testing framework • Git If you need help with these, Heroku's Quickstart Guide has links to help you out. A Note on Base 36 Numbers In the above example, you might be wondering how we go from khw to 26564. Well, khw is a base 36 number. If you're familiar with hexadecimal color codes, you'll be familiar with the idea of a base 16 counting system. That's where digits can not only be between 0 and 9, but can also be A (which corresponds to 10), B (which corresponds to 11), and so on through to F (which has a value of 15). A base 36 numbering system extends this idea so that a digit can be anything between 0 and 9 or A to Z. The table below gives some examples of base 36 numbers and their decimal counterparts: Base 36 number Decimal value 9 9 A 10 B 11 Y 34 Z 35 10 36 1BC 47064 Step 1: Set Up Your Environment To start, we'll need to install some Gems. Gems are Ruby libraries, and can be installed by using the gem command. We'll need to install the following gems for our application: • heroku to administer our application • rspec to test our application • sinatra to provide the framework for our application • rack and rack-test, which provide Ruby with an interface with the web server To install these Gems, run the following commands: $ sudo gem install heroku
Successfully installed rake-0.8.7
Successfully installed mime-types-1.16
...
Installing RDoc documentation for json_pure-1.4.6..
Installing RDoc documentation for heroku-1.10.5...
$ sudo gem install sinatra
Successfully installed rack-1.2.1
Successfully installed sinatra-1.0
...
Installing RDoc documentation for rack-1.2.1...
Installing RDoc documentation for sinatra-1.0...
$ sudo gem install rspec
**************************************************
Thank you for installing rspec-1.3.0
Please be sure to read History.rdoc and Upgrade.rdoc
for useful information about this release.
**************************************************
Successfully installed rspec-1.3.0
...
Could not find main page README.rdoc
$ sudo gem install rack
Successfully installed rack-1.2.1
1 gem installed
Installing ri documentation for rack-1.2.1...
Installing RDoc documentation for rack-1.2.1...
$ sudo gem install rack-test
Successfully installed rack-test-0.5.4
1 gem installed
Installing ri documentation for rack-test-0.5.4...
Installing RDoc documentation for rack-test-0.5.4..
$ Step 2: Write Some Tests Now that we have our Gems installed, we can start writing some Ruby code. Let's start with some simple tests. Create a directory called my-url-shortener. Then, inside a file called my-url-shortener-test.rb, put in the following: require "my-url-shortener"
require "spec"
require "rack/test"
set :environment, :test
describe "My URL Shortener" do
include Rack::Test::Methods
def app
Sinatra::Application
end
it "redirects to a blog post" do
get "/123" # 123 (base 36) == 1371 (base 10)
last_response.status.should == 301
last_response.headers["location"].should == "http://blogs.sitepoint.com/?p=1371"
end
it "redirect to contest with lowercase digits" do
get "/a" # a (base 36) == 10 (base 10)
last_response.status.should == 301
last_response.headers["location"].should == "http://blogs.sitepoint.com/?p=10"
end
it "redirect to contest with uppercase digits" do
get "/A" # A (base 36) == 10 (base 10)
last_response.status.should == 301
last_response.headers["location"].should == "http://blogs.sitepoint.com/?p=10"
end
it "redirects to home" do
get "/"
last_response.status.should == 301
last_response.headers["location"].should == "http://blogs.sitepoint.com/"
end
it "unrecognised path redirects" do
get "/foo/bar"
last_response.status.should == 301
last_response.headers["location"].should == "http://blogs.sitepoint.com/foo/bar"
end
end

In the above code, we're testing the following redirects: Requested path Redirect destination /123 http://blogs.sitepoint.com/?p=1371 /A http://blogs.sitepoint.com/?p=10 /a http://blogs.sitepoint.com/?p=10 / http://blogs.sitepoint.com/ /foo/bar http://blogs.sitepoint.com/foo/bar Running these tests will only work if there's a class called my-url-shortener, so let's create that. Sinatra will do all of the work for us if we just create a file called my-url-shortener.rb with the following line: require "sinatra"
Now we can run the test by entering spec my-url-shortener-test.rb at the command line: $ spec my-url-shortener-test.rb
FFFFF
1)
'My URL Shortener redirects to a blog post' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:16:
2)
'My URL Shortener redirect to contest with lowercase digits' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:22:
3)
'My URL Shortener redirect to contest with uppercase digits' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:28:
4)
'My URL Shortener redirects to home' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:34:
5)
'My URL Shortener unrecognised path redirects' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:40:
Finished in 0.020694 seconds
5 examples, 5 failures

Here we can see that all five tests have failed, and our redirector just returns 404s. Let's do something about that, shall we? Step 3: Write Your Application Add the following to my-url-shortener.rb: get %r{^/([0-9a-zA-Z]+)$} do
postid = params[:captures][0].to_i(36)
redirect "http://blogs.sitepoint.com/?p=#{postid}", 301
end
These four lines: • intercept any request matching the regular expression ^/([0-9a-zA-Z]+)$ (that is, anything starting with a slash followed by one or more alphanumeric characters), • extract a Post ID from the incoming request, converting a base 36 number into an integer, • redirect the user to http://blogs.sitepoint.com/?p=postid, and • stop handling this request Rerunning the test shows that we've made some progress: $ spec my-url-shortener-test.rb
...FF
1)
'My URL Shortener redirects to home' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:34:
2)
'My URL Shortener unrecognised path redirects' FAILED
expected: 301,
got: 404 (using ==)
./my-url-shortener-test.rb:40:
Finished in 0.0337120000000001 seconds
5 examples, 3 failures
The three dots in the first line of these results shows us that three of our tests passed. The two remaining cases can be handled by adding a catchall to the end of my-url-shortener.rb: get "/*" do
path = params["splat"]
redirect "http://blogs.sitepoint.com/#{path}", 301
end
Let's try running the tests now: $ spec my-url-shortener-test.rb
.....
Finished in 0.008923 seconds
5 examples, 0 failures
Success! Now, let's deploy this sucker to Heroku. Step 4: Deploy to Heroku In order to deploy to Heroku, we'll need to set up two config files: one called .gems, which contains a list of the Gems our application requires, and another called config.ru, which tells Heroku how to run our app. These files, like the others in this project, are super simple. Put this in .gems: sinatra

Put this in config.ru: require "my-url-shortener"

run Sinatra::Application

Now we're ready to deploy our application. If you're yet to do so, visit Heroku and sign up. Once you've done this, run through the instructions in Heroku's Quickstart Guide to create a basic Heroku application. In Mac OS X, this boils down to: $ cd ~/my-url-shortener/
$ git init
Initialized empty Git repository in /Users/craiga/my-url-shortener/.git/
$ git add .
$ git commit -m "Creating URL shortener"
[master (root-commit) b602ee0] Creating URL shortener
4 files changed, 57 insertions(+), 0 deletions(-)
create mode 100644 .gems
create mode 100644 config.ru
create mode 100644 my-url-shortener-test.rb
create mode 100644 my-url-shortener.rb
$ heroku create

Enter your Heroku credentials.
Email: craiga@ sitepoint .com
Password:
Uploading ssh public key /Users/craiga/.ssh/id_rsa.pub
Creating gentle-light-20... done
Created http://gentle-light-20.heroku.com/ | Git @ heroku .com:gentle-light-20.git
Git remote heroku added
$ git push heroku master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 940 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
-----> Heroku receiving push
-----> Sinatra app detected
-----> Installing gem sinatra from http:// rubygems.org
Successfully installed sinatra-1.0
1 gem installed
Compiled slug size is 236K
-----> Launching.... done
http:// gentle-light-20 .heroku .com deployed to Heroku
To git @heroku .com:gentle-light-20.git
* [new branch] master -> master

Boom! Your application has been deployed to Heroku. Check it out by visiting the URL Heroku gave you (in the above example, it's http:// gentle-light-20 .heroku .com). Step 5: There Is No Step Five! And that's that. If you have a short domain you'd like to use, such as the 99d.me domain we use here at 99designs, you can assign it to your website using the tools on Heroku's site. At the time of writing, you'll need to enter your credit card number, but there'll only be a charge if you need lots of database space or better performance. You can leave your comments on the blog entry: A Ten-minute URL Shortener
by Craig Anderson
________________________________________
See you next week for another issue of the Tech Times. Louis Simoneau
Techtimes @ sitepoint .com
Editor, SitePoint Tech Times ________________________________________

312 views and 0 responses