Coding with Jesse

Using POST with a regular link

Here's the problem: According to the principals of Representational State Transfer (REST), GET requests shouldn't change anything on the web server. Only POST requests should. For example, you really shouldn't have an <a> link that deletes an item. When you click a link, it sends a GET request to another page, and deleting an item is definitely a change. You should have a delete button that submits a form using POST instead.

Why? Well for one, Google Accelerator speeds up your web surfing by downloading all the pages that it knows about through the links on a page. So if there are links that delete items or other horrible things, Google Accelerator will go and delete everything for you. This actually happened to some users of 37 signals.

So the real solution is to just use a submit button for all the actions that actually do something (delete, save, etc.). And this is totally fine from a technical point of view, but not so great from an asthetic point of view. Buttons can be pretty ugly, right? (I actually like how they look, but then again, I'm not a designer.)

There is also another solution. You can use JavaScript to make a hidden form, then add an onclick event to a link that submits the form when you click the link. This solution is okay, except for those who don't have JavaScript (like search engine spiders).

I came up with a solution that tackles the problem from the asthetic angle. We can style the buttons so they look like links! Seriously!

Okay, it's not so easy. And it's not perfect. But it's pretty close. Here's the code:

.link-button {
    border: 0;
    padding: 0;
    background: inherit;
    font: inherit;

    cursor: pointer;
    text-decoration: underline;
    color: blue;

    overflow: visible;
}

<input type="submit" class="link-button" value="Delete"/>
<button type="submit" class="link-button">Delete</button>

It's a bit long because buttons have a lot of default styling to them. First, we remove the border, padding, background and font. Next, we change the mouse cursor and colour, and add an underline so that it looks and acts like a link. Last, we add this overflow: visible to force Internet Explorer to get rid of that weird spacing it adds inside buttons.

You might also have to set the actual font and size of the button to make it match the text around. I don't know why, but font: inherit just doesn't always do the job like it should. If you specifically set font: 12px Arial, it will look fine.

In Safari, it won't do anything if you're using an <input type="submit"> because you can't style these. However, you can style a <button type="submit"> though. You will just have to set the background colour directly and not use inherit. So as long as you don't have some trippy background image, it will be fine.

Another thing: it's not perfect in Firefox or Safari. It will still have some weird spacing around it. Have a look at what Firefox does:

Buttons have some spacing around them in Firefox

The top is a regular link, the bottom is a button. You can see it's pretty close, but not perfect. Safari has a similar effect with spacing on the bottom.

Well we can take it to the next level and use JavaScript so that it's perfect for the 90% of people who use JavaScript. The other 10% can see the extra spacing, but that's no big deal (they probably won't even notice). We can use the following function to accomplish this:

function replaceButtonWithLink(button) {
	var link = document.createElement('a');
	link.href = '#';

	// find link text either as value of input or innerHTML of button
	link.innerHTML = button.value || button.innerHTML;

	link.onclick = function() {
		button.form.submit();
		return false;
	};
	button.parentNode.insertBefore(link, button);
	button.parentNode.removeChild(button);
}

// here's what you do for an <input type="submit"/> or <button type="submit"/>
var button = document.getElementById("my_button");
replaceButtonWithLink(button);

Now we have a nearly perfect solution. If the spacing in Firefox isn't a problem for you, you don't even need the JavaScript portion. But it's there just in case you have some pixel-paranoid designers on your team that insist it should look perfect in their browsers (just hope they have JavaScript turned on!)

Published on November 22nd, 2006. © Jesse Skinner

Replace text with an image using CSS

Let's say you want to have a logo on a page, but you'd really like to use an <h1> with some text for the header in the HTML. Or maybe you like to use images for all your titles, but would still like to have plain text inside header tags in your HTML.

There are a number of reasons for wanting to do this, namely accessibility and search engine optimization. I talk more about this in my post Writing Semantic HTML.

The problem is: How do you hide the text? I think the most popular technique is to wrap the text with a <span> inside the header tag, then use some CSS to 1) hide the text, and 2) use the header tag as an image. Something like this:

<style type="text/css">
h1 {
    width: 500px;
    height: 100px;
    background: url(my_header_image.gif);
}

h1 span {
    display: none;
}
</style>

<h1><span>My Great Header</span></h1>

Unfortunately, this technique makes us add an extra tag to our markup, and we all know that every time we use an unnecessary tag, a puppy dies. Either that, or we end up with ugly, unnecessarily bloated HTML.

Well here's another technique which hides the text just by using CSS. The text will still be readable by screenreaders and search engine spiders, but will disappear like magic for everyone else:

<style type="text/css">
h1 {
    width: 500px;
    height: 100px;
    background: url(my_header_image.gif);
    overflow: hidden;
    line-height: 500px;
}
</style>

<h1>My Great Header</h1>

This technique works by pushing the text down inside the header with a rather large line-height (it must be at least twice the height). Then the overflow: hidden hides the text since it's overflowing.

Now isn't that better? No puppies were harmed, and we end up with slightly cleaner and shorter markup.

Published on November 8th, 2006. © Jesse Skinner

The Ajax Experience Wrap-up

I had a really great time at The Ajax Experience, and got to meet a lot of really cool fellow JavaScript hackers. The sessions were all very interesting, but what really stuck with me was the difference that a good JavaScript library can really make in your development.

John Resig's talk on Choosing a JavaScript Library showed how much cleaner and faster development can be when a library offers elegant solutions to very common problems. And I assume John used some kind of Jedi mind trick because his talk made me choose his own library, jQuery. I even rewrote my talk's examples in jQuery to make them shorter. I'm sure I'll be writing a lot about jQuery on here in the future.

My "Unobtrusive Ajax" talk went pretty well, I thought. I realised afterwards that I hadn't pointed people at my examples for download. So if you attended and want to take a second look, or if you didn't attend but still want to see what it was all about, go check out the downloads below:

And please, contact me if you have any questions or comments about any of this stuff.

Published on October 27th, 2006. © Jesse Skinner

Carnival of the Web is on hiatus

Unfortunately, there will be no Carnival of the Web this month. I'll be putting it on hiatus indefinitely. Now that I'm freelancing, I simply don't have the time to read through blogs and pick the best posts. Perhaps it will come back some time in the future. I apologize to everyone who had submitted articles for the fifth edition.

Published on October 21st, 2006. © Jesse Skinner

Defining functions in a loop

Do you ever try to define a function in a loop, like an event handler for example, and find that it's always the last variable in the array that is pointed at by every event handler? It can take a while to debug this stuff. Let's say you do something like this:

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    link.onclick = function() {
        alert(link.href);
        return false;
    };   
}

(Stupid example, I know). Well if you run this on a page, you would expect that everytime you click a link, you will get an alert with the href of that link. Well that's wrong! You actually get the href of the last link on the page every time!

This happens because of closures in JavaScript. You would expect that the link variable has the same value inside the onclick function as it does outside. But in fact, every time the loop comes around, link gets a new value, and actually the link variable in all those other onclick functions changes. So when the loop finishes, every onclick function will alert the href of the last link.

So to avoid this, you can do a few things. You can make a separate function that attaches the event like so:

function attachLinkEvent(link) {
    link.onclick = function() {
        alert(link.href);
        return false;
    };   
}

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    attachLinkEvent(link);
}

This works because inside the attachLinkEvent function, the value of link points at the argument, which is a copy of the link in the loop. This way it doesn't change when the original link changes. Confusing? You bet.

Rather than use this workaround, you can just avoid using the link variable altogether in the onclick function, and replace it with this. Inside an event handler like onclick, this will point at the element that the event handler is attached to. In this case, this points at the link:

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    link.onclick = function() {
        alert(this.href);
        return false;
    };   
}

Of course, real life examples are a bit more complicated. There are also different workarounds to fix the problem. But typically, one of these two solutions will fix things.

For way more information on closures than you will ever need, read Richard Cornford's essay JavaScript Closures

Published on October 19th, 2006. © Jesse Skinner
<< older posts newer posts >> All posts