The Holy Grail Revised
Rounded Corners with SVG and JavaScript
The Players
One of the most challenging problems in today's web development is the creation of rounded corners around certain page elements. With CSS 3 it is as easy as writing one line:
border-radius: 10px;
But unfortunately, the number of people actually seing rendered rounded corners is quite small. So it is no wonder, that there are dozens of different recipes and wannabe-memes that promise to solve this conflict elegantly. While the only elegant way is the above mentioned CSS 3 statement, I want to introduce yet another way today to create a box with rounded corners in all major browsers.
The Set-Up
The following idea consists mainly of two ingredients: JavaScript and an SVG/VML double pack for display in all but IE and IE itself. Actually, while thinking of it myself, the credit for bringing up the idea first belongs to others. I mention some I found at the end.
The line to follow is this: SVG and VML have a quite simple syntax for generating boxes with rounded corners. So we use these two to render a box of our needs and put it below the target element. We need to know the position of the target and its dimensions, that should not alter afterwards, and we have to do some z-index and position:relative
stuff, but that's it.
The Hook
For our recipe to succeed, we will make use of two JavaScript libraries:
- The famous jQuery, which hardly needs introduction (and if it does, search Google for it to find tons of tutorials and stuff), is just used to make the code more compact and readable.
- RaphaelJS is a relatively new JS library that aims to bring vector graphics to the web. Therefore it delivers VML to IE and SVG to all other browsers. Developers don't need knowledge of SVG or VML, there is quite a clean API.
With this together we start with a simple HTML document:
<html> <head> <title>Rounded corners</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <script type="text/javascript" src="http://github.com/DmitryBaranovskiy/raphael/tree/master%2Fraphael.js?raw=true"></script> </head> <body> <div id="container"> <p>...</p> <p id="rounded">This paragraph should have rounded corners as background.</p> <p>...</p> </div> </body> </html>
The p with ID "rounded"
is the one, that should be displayed on a blue background with rounded borders. Mark its container (div with ID "container"
). This we will need lateron. The two JS libraries are included from Google and github. The latter should be changed to a local copy of RaphaelJS, since github is no official mirror in the way Google's AJAX API is.
The Tale
Now comes the scripting part. We add a script section to the head of the document (or the foot, if you like it better there) and fill it with some code:
<script type="text/javascript"> /*<![CDATA[*/ $(function () { $('#container, #rounded').css ('position', 'relative'); $('#rounded').css ('z-index', '1'); // ... here goes all the following code ... }); /* ]]> */ </script>
The $
sign is an abbreviation for the jQuery object, and giving it a function is a simple shortcut for "run this, when the DOM is ready". Then we define a simple anonymous function (function() {...}
) where the rest of our modification will take place. The $ function called with a string usually traverses the DOM for elements that match this string interpreted as CSS selector. So the two following lines just set some CSS properties on the "container" and the "rounded" element.
Next, we define some variables inside our function:
var pos = $('#rounded').position (); var width = $('#rounded').outerWidth (); var height = $('#rounded').outerHeight (); var width_w_margin = $('#rounded').outerWidth (true); var height_w_margin = $('#rounded').outerHeight (true);
pos
contains the top and left position of our "rounded" p relatively to its container. width and height are self-explanatory, and the next two are width and height with margin, respectively. Then we create a simple, empty div and position it with CSS in a way, that it lies below our target and has the same dimensions:
$('#container').append('<div id="background"><'+'/div>'); $('#background').css ('z-index', '0'); $('#background').css ('position', 'absolute'); $('#background').css ('top', String(Number(pos.top)+ Number(height_w_margin-height)/2)+"px"); $('#background').css ('left', pos.left+ (width_w_margin-width)/2); var paper = Raphael ("background", width, height);
The top and left positions are a bit tricky, because we have to respect the margin of the target as well. The variable paper
is the storage for a new Raphael object, which lives inside the container. Behind the scenes, this is sufficient for generating a new SVG or VML element, inserted in the DOM tree and populated with some standard attributes like width and height, which were given with constructor call.
The Wire
Finally, we create the box and style it to meet our wishes:
var c = paper.rect (0, 0, width, height, 10); c.attr({ "fill": "#33ffff", "stroke": "none" });
The rectangular area is created with paper.rect()
. We set fill and stroke attributes to it, and we're ready. The complete JavaScript for this example is:
$(function () { $('#container, #rounded').css ('position', 'relative'); $('#rounded').css ('z-index', '1'); var pos = $('#rounded').position (); var width = $('#rounded').outerWidth (); var height = $('#rounded').outerHeight (); var width_w_margin = $('#rounded').outerWidth (true); var height_w_margin = $('#rounded').outerHeight (true); $('#container').append('<div id="background"><'+'/div>'); $('#background').css ('z-index', '0'); $('#background').css ('position', 'absolute'); $('#background').css ('top', String(Number(pos.top)+ Number(height_w_margin-height)/2)+"px"); $('#background').css ('left', pos.left+ (width_w_margin-width)/2); var paper = Raphael ("background", width, height); var c = paper.rect (0, 0, width, height, 10); c.attr({ "fill": "#33ffff", "stroke": "none" }); });
You can see a live example of the result here.
The Shut-Out
Of course, for production usage, this code should be generalized and made more efficient, but it documents the point: With quite little effort you can overcome some rendering limitations of today's browsers with JS and SVG. If it should be true, that IE9 will support SVG, you can also skip JS.
There are other problems with this method as well, e.g., that you have to handle existing background colors at the target and that one mixes CSS and JS, which becomes the next "don't" in the web developer's world. Saving or printing the document is also quite problematic. On the other hand, the solution is flexible enough to allow for very sophisticated background images like ones with scaling gradient or controllable patterns.
The Sting
As a developer you will have to choose between the pros and cons of all possible solutions for rounded boxes. With this new method I hope to have broaden the menu to choose from for you.
As mentioned above, there were other smart guys getting the same idea as me long before. What I found on Google so far were (unordered):
- Achieving Rounded Corners in Internet Explorer for jQuery UI with DD_roundies - with DD_roundies a jQuery plugin doing just the VML stuff from above
- iolumin.googlepages.com/home
- My experience about jQuery round corner using SVG+VML
- CSS/SVG Rounded corners for multiple browsers - a non-IE solution without JS
Note
You might have noticed, that the headings don't completely reflect the content they are heading. My apologies, but I just had to follow the small line of association I had during writing of this article that led me to the Sting.
- mstrehl's blog
- Login or register to post comments
Comments
#1 Nice play on the Sting
I love that movie, though it has been ages since I've seen it.
Checking out the code needed, Nifty Corners still seems to be easier to implement & maintain. Though it would be great if you could show just the code you add to the html elements.
Once you have put all the generic js into a separate file, what is the minimum amount of code needed to add rounded corners to an element?
Cheers,
Jimm @ Flights
#2 Very elegant
Nice - there are lots of ways to do rounded corners (many of which include huge number of small images!) but this seems like a nice elegant solution. If it's properly browser compatible (including IE6 onwards) then it's a great option.
The only small thing is that it's not very simple, so if you were to hand your site/code over to a less competent designers then they may struggle to understand what's going on.
Thanks for this.
Gregor