I'm Aaron and I make stuff that goes on the internets. We can be Twitter buds, and my feed is here.

Get Harvest: The best time tracking and invoicing app for web designers and developers.

Rounded Corners for Images with CSS 3 and jQuery

Jun 17

Rounded corners with border-radius is one of the most used CSS 3 enhancements, but it doesn’t reliably work on images. There’s a way around that, however. That’s what this tutorial is about. I didn’t invent this general technique, but the few examples I’ve found were either too messy or failed to work with linked images.

Ok then, let’s start with an image from a parking garage near me:

Let’s first try the easy way of adding border-radius styles to the image itself:


Those of you using Webkit browsers (excluding Mobile Safari) will see rounded corners. The rest of you will likely see no change at all. Even if we have it believe it is a block-level element, it still doesn’t work. What we’re going to do is create a new element, round its corners, and set its background to the original image.

First let’s define a basic CSS class for any image we want to apply rounded corners to.

.roundedimage {
     display: inline-block;
     -webkit-border-radius: 10px;
     -moz-border-radius: 10px;
     border-radius: 10px;
}

And the script to do the work of converting the image.

$(document).ready(function() {
	$(".roundme").each(function() {
		$(this).one("load",function() {
			var url = $(this).attr("src");
			var imageHeight = $(this).height();
			var imageWidth = $(this).width();
			$(this).css("visibility", "hidden");
			$(this).wrap('');
			$(this).parent()
				.css("width",imageWidth)
				.css("height",imageHeight)
				.css("background-image","url("+url+")");
		});
		if (this.complete) $(this).trigger('load');
	});
});

This script looks for anything with classname “roundme” and assigns a function to run when it is loaded. This function stores the source location and dimensions of the image, turns the visibility off, and wraps it with a span. This new wrapper element is then given the image’s dimensions and source URL for its background.

Some browsers do not fire the load() function if the image has been cached. This is a problem which would cause the whole technique to fail. The this.complete statement at the end addresses this issue. To prevent the load() function from potentially executing twice, all we need to do is make sure to attach the load() event handler using the one() method (it’s just like bind() only it automatically unbinds itself right after it runs the first time).

Now we just attach the “roundme” class to any image we want rounded for all browsers:


The jQuery above transforms this into:


Rad. But, what if the image is linked?


It’s cool. We wrapped it in a span so it’s still valid — hooray! I’ve already found a use for this for an upcoming mobile launch of Coolspotters.com. Hopefully others can benefit as well.

Push effect: Improving visual feedback for Digg style social voting tools with jQuery

Feb 3

digg.tools

Digging, voting, buzzing, retweeting, liking, bumping, shouting. These ubiquitous social tools have become the de facto mechanism to determine collective popularity. There’s little variation in terms of visual feedback when you engage these tools to increase an item’s popularity by one unit. Most just update the value instantly while others (Digg) perform a little fade effect.

Years ago I had done some push effects with live scoring applications for ESPN. I like this form of feedback when a numeric value increments. More recently I had created a UI for the CoolPapers “like” tool that uses a similar push effect. In this tutorial I’ll create a new social voting tool called Flippit to demonstrate this:

preview

See a demo here.

Markup

Like anything, this can be done a number of ways. There’s not much semantic relevance here, so any sensible structure will do. The only real requirement is to have enough elements for styling and animation.

293Flips
Flippit

Background & CSS

I’ll just use one sprite for this tool. The top will serve as the background for the numeric value, the bottom area gives us backgrounds for the unclicked, hover and clicked states of the action button.

spritepreview

There’s nothing too tricky or innovative in the CSS below. However, the key technique here is the overflow:hidden rule on the “votecard” class’s child div (line 10). This will effectively serve as a mask which the animating objects will move across. More on that later.

.votecard {
	background: url(images/sprite.png) no-repeat 0 0;
	padding: 4px;
	width: 63px;
	height: 43px;
	text-align: center;
}
.votecard div {
	position: relative;
	overflow: hidden;
	width: 63px;
	height: 43px;
}
.votecard em {
	display: block;
	position: relative;
	width: 63px;
	height: 33px;
	padding: 6px 0 6px 0;
	font: normal 24px/24px "Helvetica Neue","Helvetica","Arial",Sans-serif;
	color: #45453f;
}
.votecard strong {
	font-weight: bold;
}
.votecard span {
	font-size: 10px;
	line-height: 10px;
	display: block;
	color: #9a9a94;
}
a.voteaction {
	margin: 0 0 0 3px;
	display: block;
	text-indent: -9999px;
	width: 71px;
	height: 21px;
	background: url(images/sprite.png) no-repeat -3px -75px;
}
a.voteaction:hover {
	outline: none;
	background-position: -3px -54px;
}
a.voted,
a.voted:hover {
	outline: none;
	background-position: -3px -96px;
	cursor: default;
}

jQuery

Now we’ll have some real fun and wire this up.

$(document).ready(function() {
	/* create a node for the flip-to number */
	$(".votecard em").clone().appendTo(".votecard div");
	/* increment that by 1 */
	var node = $(".votecard em:last strong")
	node.text(parseInt(node.text())+1);

	function flip(obj) {
		obj.prev().find("em").animate({
			top: '-=45'
		}, 200);
		obj.toggleClass("voted",true);
	}

	$('.voteaction').bind({
	  click: function(event) {
	    event.preventDefault()
	  },
	  mouseup: function() {
	    flip($(this));
		$(this).unbind('mouseup');
	  }
	});

});

Content duplication

You might have wondered why the next number (294) was not included in the original markup shown earlier. The answer is found in line 3 through 6. This code duplicates the <em> node containing the current vote value (293) then increments that value by one. This new <em> node is forced to flow beneath the original one, but is hidden from view as specified in the CSS above. Here’s what your browser sees after this code runs:

duplication

Button events

Two events will be bound to the “Flippit” button, and you’ll see why these events are kept separate shortly. The default browser response is disabled in the click event. The mouseup event handler will call the flip() function which will perform the animation. It is important to allow this function to be called once only. After the user has initiated a “flip”, this action should be immediately disabled.

Had we grouped the event.preventDefault() together with the flip() call in a single button event (on click for example), we’d reinstate the default browser response when we unbound that event from the button. This would likely result in a page refresh if the user clicked the button twice. Keeping these events separate lets us unbind just the flipping action while preserving event.preventDefault().

Flip tha script *

The flip() function’s task is to select both <em>s and then move them up a distance of 45 pixels. Note also that this function takes as an argument a jQuery object reference to the button which called it. From this, we can traverse to select the <em>s we want to animate. The top <em> will slide out of view, while the jQuery generated one slides into view. The process is completed by toggling the button’s class name to “voted”.

What’s this good for?

This adds a little tactility to these contraptions. It reinforces the notion that you just impacted a piece of content on the web, if only by one solitary unit. If it’s good enough for sports apps, it’s good enough for anything else.

This technique could be used for anything where a numeric value increases or decreases whether it results from user interaction or not (as in live scoreboards).

View the demo (Works in IE6, IE7, IE8, Safari 4, Chrome, Firefox 3.6)

Download files

Pretty checkboxes with jQuery

Apr 20

Update (Jan 30, 2010):

I’ve rewritten some of this so it is valid for XHTML 1.0 Transitional. I also fixed a bug whereby the “pretty” version of the form did not recognize preselected checkboxes.

The core method I show below would also work great for radio buttons. Bagwan Pankaj, Sean Foushee and Philip Beel all have since offered great tutorials on how to extended this concept to radio buttons.

I was working on a project recently where form checkboxes were required, but the standard UI would not do. I needed something a little more engaging. So in this short tut, I’ll show how to turn this…

picture-5

…into this

picture-9

Using some CSS and jQuery. You can see this simple demo here. Those new to jQuery should check out these tutorials and resources.

I’m sure you’re psyched. Let’s get started…

1. Form markup

So normally you’d have some structure like this:

Choose some stuff...

All well and good, but we need more. We will use list items to hold the checkboxes, content and controls:

Choose some stuff...

The checkboxes are still there, but we won’t be clicking them. The checking/unchecking will handled by the “Select” and “Cancel” links within each list item.

2. Backgrounds

Each list item has a selected (green) and deselected (grey) state. This is most easily taken care of by using one background image, and shifting the position when needed. Fire up Photoshop and create these images:

picture-8

The list items will be 105 pixels wide and 150 pixels tall, so checkboxbg.gif should be 210 pixels wide (double the width). When it is selected, the background will shift to the left by 105 pixels to expose the green side. We’ll get to the CSS for this shortly.

Sendit.gif and select.gif will serve as backgrounds for the submit button and select link, respectively.

3. CSS

legend {
	font-size: 17px;
}

fieldset {
	border: 0;
}

.checklist {
	list-style: none;
	margin: 0;
	padding: 0;
}

.checklist li {
	float: left;
	margin-right: 10px;
	background: url(i/checkboxbg.gif) no-repeat 0 0;
	width: 105px;
	height: 150px;
	position: relative;
	font: normal 11px/1.3 "Lucida Grande","Lucida","Arial",Sans-serif;
}

.checklist li.selected {
	background-position: -105px 0;
}

.checklist li.selected .checkbox-select {
	display: none;
}

.checkbox-select {
	display: block;
	float: left;
	position: absolute;
	top: 118px;
	left: 10px;
	width: 85px;
	height: 23px;
	background: url(i/select.gif) no-repeat 0 0;
	text-indent: -9999px;
}

.checklist li input {
	display: none;
}

a.checkbox-deselect {
	display: none;
	color: white;
	font-weight: bold;
	text-decoration: none;
	position: absolute;
	top: 120px;
	right: 10px;
}

.checklist li.selected a.checkbox-deselect {
	display: block;
}

.checklist li label {
	display: block;
	text-align: center;
	padding: 8px;
}

.sendit {
	display: block;
	float: left;
	top: 118px;
	left: 10px;
	width: 115px;
	height: 34px;
	border: 0;
	cursor: pointer;
	background: url(i/sendit.gif) no-repeat 0 0;
	text-indent: -9999px;
	margin: 20px 0;
}

As I mentioned above, the effect of adding a class of “selected” to the list item shifts the background image, hides the “Select” link and shows the “Cancel” link. The checkbox itself is also hidden entirely (but it needs to be present in the markup).

4. jQuery

Here’s where we weave this together.

$(document).ready(function() {

	/* see if anything is previously checked and reflect that in the view*/
	$(".checklist input:checked").parent().addClass("selected");

	/* handle the user selections */
	$(".checklist .checkbox-select").click(
		function(event) {
			event.preventDefault();
			$(this).parent().addClass("selected");
			$(this).parent().find(":checkbox").attr("checked","checked");

		}
	);

	$(".checklist .checkbox-deselect").click(
		function(event) {
			event.preventDefault();
			$(this).parent().removeClass("selected");
			$(this).parent().find(":checkbox").removeAttr("checked");

		}
	);

});
});

Line 4

Looks to see if there are any pre-checked checkboxes, and assigns the appropriate class if there are any.

Line 7

This selector grabs all those green “Select” buttons and assigns a script to the click event.

Lines 9, 10, 11

We want to prevent the link from invoking its default behavior of reloading the page, and continue with our instructions.  The $(this) object refers to the “Select” button itself:

Select

So $(this).parent() refers its parent in the DOM, which is the <li> element. This is the element we want to add a class of “selected” to. Finally, line 11 makes sure that the checkbox is actually selected by setting its “checked” attribute.

Line 16

This selector catches all the “Cancel” links (which become visible when the list item’s class is set to “selected”).

Lines 18, 19, 20

This essentially undoes everything we did in lines 9, 10 and 11 above.

So what can you do with this?

Anything you want. You can keep things simple and familiar by creating custom designed checkboxes, or take a more innovative approach and employ a more creative multiple selection UI. You could even create a toggle switch like those found in the iPhone settings. This tutorial is more of a starting point. As I mentioned above, there are some other tutorials you can check out based on this concept applied to radio buttons.

View the demo

Download files