Update: jQuery Plugin for Retaining Scalable Interfaces with Pixel-to-Em Conversion

Posted by Scott on 05/20/2009

Topics:

Using em units in CSS layout allows for page components to scale in unison with a user's font-size preferences. But developing in ems within dynamically updating web apps can be very tricky. In an earlier lab article, we released a script to quickly convert pixel values to ems (and vice versa) to support page scalability throughout a web site or application. We've rewritten the plugin to better follow jQuery syntax and reduce code weight, and are now releasing an updated version for download.

Why ems, anyway?

In CSS layouts, ems are often used in place of pixels to allow elements and text to scale in unison. Although pixels tend to be easier to use and more precise, many browsers such as Internet Explorer can not scale text that is set in pixels. By using ems for not only our text, but for setting the heights and widths of structural elements, some or all of the page layout can also scale in proportion to the text size.

How do ems work?

In brief, an em is a relative unit that is equivalent to the height of a font. Since most browsers' default font size is 16px, we can assume that 1em is equal to 16px; meaning that 12px is equal to 0.75em, and 10px type is equal to 0.625em. As you can see, it can be quite tedious to calculate em values this way, and luckily a method was developed by Richard Rutter to alleviate the problem. By simply setting the body element's font size to 62.5%, the value of 1em becomes 10px. This allows for nice and easy calculations to ems by simply dividing a pixel value by 10.

While that cuts down on some of the conversion work, the value of 1em can still vary depending on parent/child relationships in the DOM. For every element on the page, the value of 1em is calculated based on the font size of its parent element. This means that while a paragraph may have a font size of 1.2em and appear to be 12px tall, all of its children will exist in a world where 1em is equal to 12px instead of 10. Which, in a way, leads us back to where we started.

Where ems are often abandoned

Developing an em-based stylesheet can be confusing, but it's manageable once you understand the complexities of font-size inheritance. We find that the problems more often occur outside of the stylesheet, when elements are inserted or modified with javascript. During animation effects or modifications to the DOM, many javascript libraries style elements using pixels to avoid the possibility of conflicts, and while pixels are often desired and are a good default, we usually want our modifications to fit within a scalable interface. For this reason, we've come up with a method that converts pixel values to ems on the fly, and always calculates within the scope it is given.

So how does the plugin work?

When called, our plugin converts a given pixel value to ems. By default, it uses the body's font size for scope, but a scope property (in the form of a jQuery selector) can be passed to handle conversions that are relative to that element's font size.

Sample output:


var pixelValue = 256; 
var emValue = $(pixelValue).toEm(); 
==> returns '25.6em'

The sample above may look like a simple division of 10, but the script actually checks the font size of the body before calculating so it will work in many environments. To demonstrate how scope is used, consider the following example which uses a scope property:

Sample output using a scope element:


var pixelValue = 256;
var scopeElement = $(myElement); //element being used for scope. It has a font size of 14px.
var emValue = $(pixelValue).toEm({'scope': scopeElement}); 
==> returns string "18.29em"

We can see that pxem.jQuery.js has used the font size of the scope element for its calculation above. Using a scope parameter may be particularly useful for figuring out dimensions when inserting an element into the scope element.

But what about ems to pixels?

Don't worry, we didn't leave you hangin'! A second included plugin called 'toPx' defines the direction of the conversion. Simply call the .toPx method instead and the script will convert your em value to pixels. Keep in mind that the plugin expects a number value, not a string, so use 7.5 instead of '7.5em'. The following demo demonstrates the flexibility and capabilities of this plugin.

For example

As an example, let's assume we're injecting a div into some nested markup and it needs to appear 80px wide, while set using ems. Here's our sample markup and CSS:

HTML:


<div class="A">
	<div class="B">
		<div class="C">
			<div class="D"></div>
		</div>
	</div>
</div>

CSS:


body{ font-size: 62.5%; }
.A { font-size: 2em; }
.B { font-size: 2em; }
.C { font-size: .5em; }
.D { font-size: 8.5em; }

If you recall from above, em font-sizes are calculated based on their entire parent hierarchy, so depending on where we inject our div in the markup above, its em-based width will need to be quite different in order to appear 80px wide. For example, if we inject it into div.A, we would need to calculate an em value based on half that of what it would be if it were injected into the body, due to its 2em font size. Furthermore, if we were to inject the div into div.D, we would need to calculate our width based on 8.5 x .5 x 2 x 2, due to all of the inherited font sizes in the nested divs.

Thankfully, this plugin handles all of the complex calculation logic for us. For example, to get the appropriate width value for injecting in div.A, we could do this:


$(80).toEm({scope: '.A' });

...which would return '4em', giving us the width we could then set on our div.

Likewise, to get our width value for injecting into div.D, we could do this:


$(80).toEm({scope: '.D' });

...which would return '0.470588em;'. A very different value, but equal to 80px in this context nonetheless.

Visual demo

The demo below contains the markup and CSS shown above. Here’s what’s happening: Using jQuery, we append a new div (shown in gray below) to each existing div, and use our plugin to calculate em-equivalent 200px, which we use to set that div's width. The value of 1em varies depending on the font-size inheritance of the parent element into which the div is injected. The plugin script factors in that font-size inheritance and calculates a necessary multiplier value to make all divs visually equal in width, while their actual width values vary greatly (as shown in the labels below).

Demo page

Sweet! How do I use it?

First, you'll need the jQuery javascript library, which is free and can be downloaded at jQuery.com. Then grab one of the javascript file linked below and either paste its contents into your javascript file or link to it as a standalone.

Download (and help us improve) the code

The pixel-to-em plugin code is open source and available in a git repository, jQuery-Pixel-Em-Converter. If you think you can help on a particular issue, please submit a pull request and we'll review it as soon as possible.

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site

Comments

Thanks; this looks awesome.

Will certainly be using this in my next project.

Comment by Ben on 05/21  at  07:45 PM

would not it be better to return a number for further calculations, like adding or removing padding/margin widths??

any way, it is really worth the additional effort, only for scaling and only in IE?? the only people that i know that know that there is a scaling feature in the browser are all developers…

Comment by Michael Moossen on 05/26  at  09:41 AM

This is a really useful plugin but had a question in regards to the API. The hardest part for me was wrapping my head around passing in a pixel value as the selector. Would think the following would make more sense:

var em = $(’body’).toEm( 256 );

Then you could accept a second attribute that would not return the unit of measure (to allow for easier calculations):
var em = $(’body’).toEm( 256, false ) // return 25.6 instead of 25.6em

Cheers,
- Jonathan

Comment by Jonathan Sharp on 07/22  at  11:02 PM

Brilliant !

Comment by Quintin on 09/02  at  10:25 AM

@Michael Moossen

You obviously have never done any work in accessibility. Also, what about liquid layouts?

Comment by Antony Kennedy on 10/15  at  05:25 PM

@Michael Moossen: Good question. Returning just a number might be handy, but then you’d have to concatenate it with +’px’ or +’em’ every time. We did it this way since we almost always used it to translate a result, rather than using it in a step along the way in a calculation.
As for your question about users knowing about browser zoom features, I guess it’s hard to make assumptions about that, but we tend to write CSS in flexible units so pages at least scale vertically when resized. It doesn’t end up causing any additional development time to code this way, and often allows us to design proportionally so elements can be scaled through parent font size if we need to make large-scale adjustments.

@Jonathan: nice idea. Maybe something we should consider…

Comment by Scott (Filament) on 10/15  at  06:15 PM

@Antony Kennedy: indeed i have not done much work focused on accessibility. but what has that to do with this? could you please be a little bit more constructive?

@jonathan: i also like the idea

@scott: yes, of course, most of the time you (and always more, i) will use proportional sizes, but there are exceptions: in my case, a 5px wide, white frame (margin) around a thumbnail… or the border of this very textarea where all measures are in em except the 1px border!
On the browser’s zoom feature, i was just telling my experience.

Comment by Michael Moossen on 10/17  at  03:34 PM

I loved the blog you! Are always new subjects and interests of leitores.Espero always bringing new material.
Are de parabens! Success
hug

Comment by Moda feminina on 01/20  at  05:04 PM

Running your sample code in Firefox 3.6 returns 21.333333 not 25.6. This would be extremely useful if it were accurate. I’m trying to set the width of my buttons based on the number of characters in them. Everything is working except multiplying my string length by the ems calculation. I need to multiply by the body font-size, but jquery without a plugin like this returns it in pixels, which is then not scalable. My font-size is set as .74em in my stylesheet, this jquery returns the body font-size as 11.833333px, when I strip the “px” and convert to ems it returns .916666667 instead of .74. Why?

Comment by BenDZN on 03/29  at  06:37 PM

I have the same problem in IE vs. Firefox.  JQuery returns my header font-size as 20px in IE and 18px in Firefox, so the pixel to em calculation is off for the purposes of resizing.  Incidentially, the Firefox calculation is correct as per my stylesheet.  Does anyone know how to fix this?

Comment by Jamie on 06/11  at  09:46 PM

Why append a temp div with font-size 1em and get its height?  Why not just divide the pixel value by parseFloat($element.css("font-size")) where $element is the body by default?

Comment by David on 04/14  at  12:53 AM

As for your question about users knowing about browser zoom features, I guess it’s hard to make assumptions about that, but we tend to write CSS in flexible units so pages at least scale vertically when resized. It doesn’t end up causing any additional development time to code this way, and often allows us to design proportionally ..

Comment by Good Online Casino on 05/28  at  04:49 PM

Commenting is closed for this post.

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site