Udostępnij za pośrednictwem


Sub-pixel Rendering and the CSS Object Model

With Windows 8, you have an unprecedented choice of devices for browsing the Web, from large desktop screens to small slates. In order to accommodate this range of devices, the browser must be able to scale and layout the Web at many different screen sizes and dimensions. We've previously blogged about some of the features in IE that support these scenarios. Sub-pixel positioning (of text and layout) is one of the core platform technologies which enable Web pages to look beautiful and consistent at any scale.

In this post, we describe changes made in IE10 to better support sub-pixel positioning through the CSS-OM.

Web developers can build beautiful layouts through a variety of platform technologies. Generally, developers use CSS style sheets to describe the layout a Web site. In some scenarios, Web developers also depend on JavaScript code to measure, align, or position elements of a Web page with pixel-perfect precision. For example, some online editors carefully position an edit box exactly over the top of existing content so that it only appears as if you are directly editing the existing content. Scenarios such as this one may use the CSS object model (CSS-OM) APIs to read and/or set element position. The CSS-OM is a set of JavaScript APIs for programmatically manipulating CSS.

Measuring and aligning layout elements using the CSS-OM APIs can be problematic because of the way that those APIs round or truncate sub-pixel positioned values to whole-pixel numbers.

A Quick Note about Pixel-perfect Layouts

In general, pixel-perfect layouts on the Web tend to conflict with the goals of enabling accessible, compatible, and adaptable content, and consequently are not a best practice. The following collage illustrates some of the bugs that can occur when the Web designer attempts to create a pixel-perfect design but unexpected Web platform differences cause their designs to go awry.

Examples of pixel-perfect designs gone wrong
Examples of pixel-perfect designs gone wrong

When using the CSS-OM to dynamically generate a layout, Web developers should allow for a few pixels of potential error. Better yet, IE10 provides several new layout options that developers can use to better achieve many of these desired layouts without requiring pixel-perfect alignment using the CSS-OM.

An Illustration

To illustrate how the CSS-OM APIs may cause subtle positioning problems, consider a simple example. Sub-pixel positioning allows four boxes to be evenly distributed inside of the container, despite the container's size not being evenly divisible by four.

Consider the following HTML markup fragment:

<footer>

<div>content 1</div><div>content 2</div><div>content 3</div><div>content 4</div>

</footer>

with this partial CSS markup:

footer { width: 554px; border: 1px solid black; text-align: center; }

footer div { display: inline-block; width: 25%; }

footer div:nth-child(even) { background-color: Red; }

footer div:nth-child(odd) { background-color: Orange; }

Now, let’s add a function that runs on load and reports the widths of these items:

onload = function () {

var footerBoxes = document.querySelectorAll("footer div");

var s = "";

var totalSize = 0;

for (var i = 0; i < footerBoxes.length; i++) {

// Reporting

var offsetWidth = footerBoxes[i].offsetWidth;

s += "content " + (i + 1) + " offsetWidth = " + offsetWidth + "px" + "<br />";

totalSize += offsetWidth;

}

s += "Total <i>calculated</i> offsetWidth = " + totalSize + "px" + "<br />";

s += "Container width = " + document.querySelector("footer").clientWidth + "px" + "<br />";

document.querySelector("#message").innerHTML = s;

}

The result of running this markup and code in IE9 is something like this:

content 1content 2content 3content 4 content 1 offsetWidth = 139content 2 offsetWidth = 139content 3 offsetWidth = 139content 4 offsetWidth = 139Total calculated offsetWidth = 556Actual container width = 554

Note that summing the values returned by the CSS-OM API offsetWidth results in the total calculated offsetWidth differing from the actual container width by two pixels, due to rounding that occurs in the offsetWidth of the individual div elements.

The results in other browsers show a similar discrepancy, though sometimes the total is less than the actual and sometimes greater.

When the rounding/truncation leads to a sum total that overflows the container size (as illustrated), content will start to wrap or cause unwanted scroll-bars to appear. Additionally, many containers are sized according to the text and the font used to render the text, and those font metrics may be different across browsers or alternate fonts may be selected if the requested font isn't available.

The offsetWidth API, along with many other widely used CSS-OM properties (most dating back to IE4 in 1997), provide a convenient and fast way to extract integer pixel values for an element from a variety of different coordinate systems. All major browsers implement most of these APIs for compatibility and they are also part of the CSS-OM View module, a W3C draft standard.

New browser features continue to demonstrate the shortfall of limiting CSS-OM properties to whole-value pixels. For example, features like SVG and CSS 2D/ 3D Transforms allow an element's dimensions to easily fall between pixels.

Addressing the Problem

Recognizing this limitation (in part), the W3C CSS-OM View spec describes the coordinates returned via the getBoundingClientRect() API to be floats; in other words, in values that can represent sub-pixel precision. (getBoundingClientRect() is another CSS-OM API that provides an element's bounding-box position and dimension using the same origin as the offsetWidth API.)

In IE10 we've updated the getBoundingClientRect() API to return sub-pixel resolution by default in IE10 standards mode in order to be interoperable with other browsers and align with the W3C standard.

Updating our example above to report the width component of the rectangle returned by getBoundingClientRect(), IE10 now reports these fractional values:

content 1content 2content 3content 4 content 1 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 2 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 3 offsetWidth = 139, getBoundingClientRect().width = 138.5 content 4 offsetWidth = 139, getBoundingClientRect().width = 138.5 Total calculated offsetWidth = 556 Total calculated getBoundingClientRect().width = 554 Actual container width = 554

Taking Sub-pixel Values Everywhere

In addition to getBoundingClientRect, we also report sub-pixel position for mouse/pointer events in IE10 standards mode by default. It's only possible for the mouse/pointer to land between pixels when the zoom factor is set to a value other than 100%. Specifically, the affected mouse/pointer APIs are:

  • MouseEvent.offsetX/Y
  • MouseEvent.layerX/Y
  • MouseEvent.clientX/Y
  • MouseEvent.pageX/Y
  • MouseEvent.x/y

To continue our commitment to compatibility with legacy Web pages (pages which may not be prepared to handle sub-pixel values from the CSS-OM in general), IE10 continues to reports whole-value pixel units by default for the other CSS-OM properties. These APIs are:

  • Element.clientHeight
  • Element.clientWidth
  • Element.clientLeft
  • Element.clientTop
  • Element.scrollTop
  • Element.scrollLeft
  • Element.scrollWidth
  • Element.scrollHeight
  • HTMLElement.offsetWidth
  • HTMLElement.offsetHeight
  • HTMLElement.offsetTop
  • HTMLElement.offsetLeft
  • TextRange.offsetLeft
  • TextRange.offsetTop

However, if necessary, IE10 now allows Web developers to enable sub-pixel positioned values from the above-listed CSS-OM properties as well. Use of this special feature requires that the site run in IE10 Standards mode and opt-in by setting the following property on the document object:

document.msCSSOMElementFloatMetrics = true;

When enabled by setting document.msCSSOMElementFloatMetrics to true, all CSS-OM APIs in the previous list will begin reporting their values in sub-pixel precision that will reflect exactly the calculations used internally by the layout and rendering engine. Note that JavaScript will convert 1.00 into 1 so you may not always see a decimal point in returned values.

Going back to our example and setting document.msCSSOMElementFloatMetrics to true results in this in IE10:

content 1content 2content 3content 4 content 1 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 2 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 3 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 content 4 offsetWidth = 138.5, getBoundingClientRect().width = 138.5 Total calculated offsetWidth = 554 Total calculated getBoundingClientRect().width = 554 Actual container width = 554

Note the fractional values returned by offsetWidth and that all totals now match.

Summary

It is sometimes helpful (and, in rare cases, necessary) to be able to use the CSS-OM properties for pixel-perfect layout calculation. When using the CSS-OM, be aware of the whole-pixel-reporting characteristics of those APIs. When designing layouts either with CSS or the CSS-OM APIs, be sure to accommodate a few pixels of tolerance in order to ensure a more robust site across multiple browsers and/or devices.

—Travis Leithead, Program Manager, Internet Explorer

Editor's note: Updated to correct a few typographic errors.

Comments

  • Anonymous
    February 17, 2012
    I think I caught a few typos or issues in this post. 2nd paragraph: "In this <strike>we</strike> post, [we] describe changes..." 3rd paragraph: "Generally, developers use CSS style sheets...". Isn't this redundant? Expanding CSS would give you "cascading style sheets style sheets", which sounds silly. How about just "Generally, developers use CSS..."? 3rd paragraph in <b>Taking Sub-pixel Values Everywhere</b> section: "Use of this special feature requires IE10 standards mode and that a site <strike>to</strike> opt-in..."

  • Anonymous
    February 17, 2012
    Your example tables should be graphics from IE10 or else nobody can see the difference unless they are already running IE10. The WPF & Silverlight debates made it clear a lot of people really don't like sub-pixel positioned text at small sizes as it looks very fuzzy. [)amien

  • Anonymous
    February 17, 2012
    In winjs/js/ui.js there is:        getRelativeLeft: function (element, parent) {            /// <summary locid="89">            /// Gets the left coordinate of the element relative to the specified parent.            /// </summary>            /// <param name="element" domElement="true" locid="90">            /// Element whose relative coordinate is needed.            /// </param>            /// <param name="parent" domElement="true" locid="91">            /// Element to which the coordinate will be relative to.            /// </param>            /// <returns locid="92">            /// Relative left co-ordinate.            /// </returns>            if (element === null)                return 0;            var left = element.offsetLeft;            var e = element.parentNode;            while (e !== null) {                left -= e.offsetLeft;                if (e === parent)                    break;                e = e.parentNode;            }            return left;        }, see e.g. code.msdn.microsoft.com/.../Simple-Imaging-Sample-a2dec2b0 What went wrong? (hints: ancestor, offsetParent, getBoundingClientRect) good luck, g

  • Anonymous
    February 18, 2012
    IMHO, A step in the right direction, "whole-pixel"-oriented thinking has to go away! But I wonder if the precision of floats is enough or will be enough in the future, sure in 3d graphics everything is done in floats but then there the user might care less about precision.

  • Anonymous
    February 18, 2012
    Does this mean that ClearType (aka Fuzzy Type) will also be fixed in Windows8/IE10?  we've been waiting for that fix for years! Can't wait to have actual BLACK text on a WHITE background instead of Gray text with a rainbow border on a non pure white background.  If we really wanted Fuzzy Type, we would take a screenshot of our clean, perfect pages with ClearType turned off, then save them as JPEG's with about 70% quality.  Nothing like horrible pixelation and un-necesary anti-aliasing to get really hard to read text with less contrast than designed. I hope whoever came up with ClearType has been fired - biggest blunder in Microsoft's history after MS Bob... and waiting 6 years after releasing IE6 to ship a browser. So glad Mobile is WebKit only (or Firefox/Opera if you download an additional browser).  Thankfully we don't need to support Trident for mobile/tablet devices.

  • Anonymous
    February 18, 2012
    I wonder whether trying to use sub-pixel rendering in web pages is useful or not. Then again, I think I now know that giving a bit of leeway could make things work even better, huh? @Jim You might want to tweak your ClearType settings. If you're using Windows 7, look at Control PanelAppearance and PersonalizationDisplay for the option. That is not exactly what's supposed to happen, since not all LCD screens are the same. Also, they are not designed to be viewed blown up. Just saying.

  • Anonymous
    February 21, 2012
    It's called a Tablet or a Pad.  Absolutely no one calls it a slate... What century is Microsoft living in!? There's also zero Microsoft tablets on the market currently that support windows 8... Available for public purchase.  You're about 3 years too late to the Tablet game Microsoft.... Collect up your marbles, wipe away your tears, and go on home now.

  • Anonymous
    February 22, 2012
    Thanks, the ability to get/set floats on elements is a huge improvement, when the positioning is done via Javascript. Especially for zooming and animation!

  • Anonymous
    February 29, 2012
    @joe: If they called it a Pad, chances are that Apple's lawyers would open their lids, spread their wings and fly north to Redmond to admonish their foe. FWIW, I think that "slate" is a more accurate term for such devices - sheets of slate are typically a lot thinner and lighter than tablets ;)