Browsers Bugs, Tips, and Tricks

Contents

  1. 1 Rationale 
  2. 2 CSS
    1. 2.1 Hiding Content
      1. 2.1.1 Workaround for table borders showing through on IE
      2. 2.1.2 Hidden content taking space on the page
      3. 2.1.3 Workaround for peek-a-boo issue on IE
      4. 2.1.4 Cell content remains hidden on IE – Simple case
      5. 2.1.5 Cell content remains hidden on IE – Inner tables
      6. 2.1.6 Visibility: hidden can be overridden by a descendant
    2. 2.2 Absolute and relative positioning
      1. 2.2.1 Box expanding to use the remaining vertical space
      2. 2.2.2 IE6 and IE7: z-index not working inside a position: relative block
      3. 2.2.3 Using position: relative for a div to act as containing block
      4. 2.2.4 IE6: position: absolute div acquiring display: inline
      5. 2.2.5 Absolute positioning below a float
      6. 2.2.6 Absolutely positioned box inside a box with overflow: auto or hidden
      7. 2.2.7 Absolutely positioned box inside a overflow: auto or hidden positioned box
      8. 2.2.8 Width property inside an absolutely positioned box
      9. 2.2.9 Aligning an absolutely positioned box in a table cell
      10. 2.2.10 Firefox doesn't support position: relative on table cells
      11. 2.2.11 Position: relative inside overflow: hidden
      12. 2.2.12 Create a block that extends to the bottom of the viewport
      13. 2.2.13 IE 7 peek-a-boo with relative box, inside a fieldset, inside an absolute box
      14. 2.2.14 IE 7 checkboxes display glitch within <ul>
    3. 2.3 Floats
      1. 2.3.1 Prevent a box from extending below a float
    4. 2.4 Sizes
      1. 2.4.1 What unit to use for sizing (em, px, %)?
      2. 2.4.2 Sizes in em for IE 6/7 vs. other browsers
    5. 2.5 Form elements 
      1. 2.5.1 Image not shown in a button on IE7 with Bootstrap
      2. 2.5.2 Padding in buttons
      3. 2.5.3 Buttons and width
      4. 2.5.4 Buttons and text indent
      5. 2.5.5 Replacing the text in a button by an image
      6. 2.5.6 Vertically aligning an image next to a form control
    6. 2.6 line-height and vertical-align: middle
      1. 2.6.1 Center content vertically inside a box
      2. 2.6.2 line-height has no effect on IE7 when no text is present
    7. 2.7 Inline blocks
    8. 2.8 Overriding CSS rules
    9. 2.9 IE: missing borders
    10. 2.10 IE6 to IE9 limit of 31 CSS files
    11. 2.11 Firefox: vertical-align: baseline stops working when using overflow: hidden
  3. 3 HTML and the DOM
    1. 3.1 Specifications
    2. 3.2 Get elements with prefixes
    3. 3.3 Which elements are focusable?
    4. 3.4 Multiple selection lists, value change, and focus event on Firefox
    5. 3.5 Closing </script> tag
    6. 3.6 In IE, nodes become disconnected when removed from the DOM with an innerHTML
    7. 3.7 Change event on Firefox 2 for Mac OS X
    8. 3.8 Change event for typed inputs on iOS
    9. 3.9 Getting the value of custom attributes with IE
    10. 3.10 getAttribute
    11. 3.11 Setting the value of number attributes
    12. 3.12 Preserving the DOM on browser back
    13. 3.13 Restoring form fields on back
  4. 4 JavaScript
    1. 4.1 Prototypes and constructors
    2. 4.2 Build your JavaScript classes so the objects have meaningful constructor
    3. 4.3 Setting the focus on IE
    4. 4.4 Optional groups in regular expressions
    5. 4.5 Chrome's DOM Exception 8
    6. 4.6 IE form element name/id mixup
    7. 4.7 YUI
      1. 4.7.1 JavaScript error when creating a YUI Panel with ARIA
  5. 5 jQuery Mobile
    1. 5.1 Change events swallowed
  6. 6 Browser hangs
    1. 6.1 IE: Calling focus() inside an iframe

Rationale 

This page discusses weird browser behaviors that made us (more than once!), bang our head against the wall. We hope that you, your head, and your wall, will find this useful.

A lot the idiosyncratic behaviors discussed here are due to browser bugs, and a lot of those browser bugs are in IE, hence the title of this page.

CSS

Hiding Content

Workaround for table borders showing through on IE

On IE6 and IE7, when hiding a table with visibility: hidden, position: absolute, the borders of the table can "show through". The following CSS avoids this.

top: -10000px;
left: -10000px;

Hidden content taking space on the page

When content is hidden with visibility: hidden, the browser will size the scroll bars as if the content was visible. So if you have a large section of the page that is hidden, the vertical or horizontal scrollbars might allow you to move further down or further right than necessary. The CSS in the previous section also solves this issue.

Workaround for peek-a-boo issue on IE

Hiding sections of the page after the page is loaded with visibility: hidden; position: absolute causes display problems on IE. (For instance with the controls example, where some form controls are not showing when going back and forth between tabs.) So at "runtime" we hide sections of the page with display: none. This seems to go again the first item above, but doesn't because we only need visibility and position to be used initially when the page loaded so the controls can be correctly initialized. Then at runtime, it is OK to use display: none. One downside of this technique on Firefox is that if a section containing a Flash movie is hidden and then shown again, the movie will start again.

.xforms-case-deselected-subsequent {
     display: none;
}

Cell content remains hidden on IE – Simple case

Source code showing the issue:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/event/event-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/dom/dom-min.js" type="text/javascript"></script>
        <script type="text/javascript">
            YAHOO.util.Event.onDOMReady(function () {
                YAHOO.util.Event.addListener("show", "click", function() {
                    YAHOO.util.Dom.removeClass("section", "hidden");
                });
            });
        </script>

        <style type="text/css">
            .hidden {
                visibility: hidden;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <p>
            <button id="show">Show</button>
        </p>
        <div id="section" class="hidden">
            <table>
                <tr>

                    <td>gaga</td>
                </tr>
            </table>
        </div>
    </body>
</html>


To see the issue:
  1. Load this example on IE6 or IE7.
  2. Click the show button once, and the text doesn't become visible.
  3. (Interestingly, if you click on the button a second time, the text becomes visible.)
The nudge hack does the trick in this case (i.e. element.className = element.className after removing the class). So this is what we are doing in the code after removing the class xforms-disabled for IE6 and IE7. The following is a modified version of the above code with the fix in place:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/event/event-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/dom/dom-min.js" type="text/javascript"></script>
        <script type="text/javascript">
            function nudgeAferDelay(element) {
                if (YAHOO.env.ua.ie != 0 && YAHOO.env.ua.ie <= 7) {
                    window.setTimeout(function() {
                        element.className = element.className;
                    }, 100);
                }
            }
            YAHOO.util.Event.onDOMReady(function () {
                YAHOO.util.Event.addListener("show", "click", function() {
                    YAHOO.util.Dom.removeClass("section", "hidden");
                    nudgeAferDelay(YAHOO.util.Dom.get("section"));
                });
            });
        </script>

        <style type="text/css">
            .hidden {
                visibility: hidden;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <p>
            <button id="show">Show</button>
        </p>
        <div id="section" class="hidden">
            <table>
                <tr>

                    <td>gaga</td>
                </tr>
            </table>
        </div>
    </body>
</html>

Cell content remains hidden on IE – Inner tables

The method described above doesn't do the trick if the section you are showing contains tables. The following will fail to show the content of the table when the Show button is activated:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/event/event-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/dom/dom-min.js" type="text/javascript"></script>
        <script type="text/javascript">
            function nudgeAferDelay(element) {
                if (YAHOO.env.ua.ie != 0 && YAHOO.env.ua.ie <= 7) {
                    window.setTimeout(function() {
                        element.className = element.className;
                    }, 100);
                }
            }
            YAHOO.util.Event.onDOMReady(function () {
                YAHOO.util.Event.addListener("show", "click", function() {
                    YAHOO.util.Dom.removeClass("section", "hidden");
                    nudgeAferDelay(YAHOO.util.Dom.get("section"));
                });
            });
        </script>

        <style type="text/css">
            .hidden {
                visibility: hidden;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <p>
            <button id="show">Show</button>
        </p>
        <table>
            <tr id="section" class="hidden">
                <td>
                    <table id="innertable">
                        <tr>
                            <td>gaga</td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
</html>

You can get around this problem by having nudgeAferDelay() not only nudge the element you are showing, but every table it contains. The following code will work on IE6 and IE7:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/event/event-min.js" type="text/javascript"></script>
        <script src="http://yui.yahooapis.com/2.7.0/build/dom/dom-min.js" type="text/javascript"></script>
        <script type="text/javascript">
            function nudgeAferDelay(element) {
                if (YAHOO.env.ua.ie != 0 && YAHOO.env.ua.ie <= 7) {
                    var tables = element.getElementsByTagName("table");
                    window.setTimeout(function() {
                        element.className = element.className;
                        for (var tableIndex = 0; tableIndex < tables.length; tableIndex++) {
                            var table = tables[tableIndex];
                            table.className = table.className;
                        }
                    }, 100);
                }
            }
            YAHOO.util.Event.onDOMReady(function () {
                YAHOO.util.Event.addListener("show", "click", function() {
                    YAHOO.util.Dom.removeClass("section", "hidden");
                    nudgeAferDelay(YAHOO.util.Dom.get("section"));
                });
            });
        </script>

        <style type="text/css">
            .hidden {
                visibility: hidden;
                position: absolute;
            }
        </style>
    </head>
    <body>
        <p>
            <button id="show">Show</button>
        </p>
        <table>
            <tr id="section" class="hidden">
                <td>
                    <table id="innertable">
                        <tr>
                            <td>gaga</td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
</html>

Visibility: hidden can be overridden by a descendant

Consider you have box 1 with visibility: hidden that contains box 2 with visibility: visible. Unlike display: none, setting visibility: hidden on a box doesn't mean that any content of that box won't show. For instance:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">

            /* Positioning */
            #box1 { visibility: hidden }
            #box2 { visibility: visible }

            /* Styling */
            #box1 { background: #666; padding: 5px; width: 125px }
            #box2 { background: #999; padding: 5px; width: 100px; height: 50px }
            body  { font-family: sans-serif }

        </style>
    </head>
    <body>
        <div id="box1">1
            <div id="box2">2</div>
        </div>
    </body>
</html>

The above code is rendered as shown in the screenshot below: box 1 doesn't show, but box 2 does. This is done according to the CSS 2.1 specification, which for the hidden value says: "[…] descendants of the element will be visible if they have 'visibility: visible'".



Absolute and relative positioning

Box expanding to use the remaining vertical space

You have 3 boxes: container, which contains content and buttons. You know the height of container and buttons, and would like content to use the remaining vertical space, as shown in:


The main constraint here is that you must know the height of the buttons box. (The name reflects the use case I have in mind, where I want to have always visible buttons at the bottom of a container box with scrollable content.) The solution below does this by absolutely positioning the content and buttons box relative to their container.

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            .container { width: 200px; height: 200px; position: relative }
            .content   { position: absolute; top: 0; bottom: 50px; left: 0; right: 0 }
            .buttons   { height: 50px; position: absolute; bottom: 0; left: 0; right: 0 }

            /* Styling */
            body       { font-family: sans-serif }
            .container { background-color: red }
            .content   { background-color: #ccc }
            .buttons   { background-color: #999;  }
            .buttons,
            .content   { text-align: center; vertical-align: middle;
                        line-height: 50px }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="content">Content</div>
            <div class="buttons">Buttons</div>
        </div>
    </body>
</html>


IE6 and IE7: z-index not working inside a position: relative block

Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">
            #rel-box { position: relative }
            #box-2   { position: absolute; z-index: 2 }
            #box-3   { position: absolute; z-index: 3 }

            /* Styling */
            #rel-box { background: #ddd; padding: 1em; width: 6em;
                       height: 3em; top: 1em; left: 2em }
            #box-2   { background: #bbb; padding: 1em; width: 4em; }
            #box-3   { background: #999; padding: 1em; top: 1em;
                       width: 4em; text-align: right }
            body     { font-family: sans-serif }
        </style>
    </head>
    <body>
        <div id="box-2">2</div>
        <div id="rel-box">
            <div id="box-3">3</div>
        </div>
    </body>
</html>

  • You have 3 boxes:

    1. position: relative box (rel-box).
    2. A position: absolute box (box-2) which is outside of rel-box.
    3. position: absolute box (box-3) which is inside rel-box.

  • You want the box-3 to show above box-2, so you assign it a higher z-index, say:
    • z-index: 2 to box-2.
    • z-index: 3 to box-3.


IE8+ and other browsers
Here you get the expected behavior: the z-index has the desired effect, and box-3 is indeed above box-2.
IE6 and IE7
Unfortunately, with IE6 and IE7, the z-index isn't honored properly.

Why?

This behavior is incorrect, according to the specification, but nevertheless, let's try to understand what is happening here. With IE6/7, the presence of a positioned box creates a new stacking context. In this example:
  1. Because rel-box is relatively positioned, IE6/7 stacks the boxes inside it independently of other boxes on the pages.
  2. Then it stacks the rel-box stack relative to other stacks on the page (box-2) based on the z-index of the stack. There is no z-index on rel-box, and there is a z-index: 2 on box-2, to it puts box-2 above the rel-box stack.
Workarounds
  • Add z-index on rel-box higher than the z-index on box-2. In this example: #rel-box { z-index: 3 }.
  • Move box-3 outside of rel-box. If in your case box-3 is an overlay such as a dialog or menu, you might just be able to put it directly inside the <body> element, which solves the issue without adding any CSS.
  • Obviously, you could also change your CSS not to relatively position rel-box. This may or may not be possible, as there might be a good reason to relatively position rel-box in your case.


Using position: relative for a div to act as containing block

Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            .container { background-color: #eee; border: 2px solid #666; width: 15em;}
            .inner     { background-color: #aaa; }
        </style>
    </head>
    <body>
        <div class="container">
            <span>In <em>container</em></span>
            <div class="inner">In <em>inner</em></div>
        </div>
    </body>
</html>

 All the browsers shows this as:


Now add a position: absolute; top: 0; left: 0 on the inner class. The inner block is taken out of the normal flow for the page, and according to the CSS 2.1 specification the top, right, bottom, and left property position the box relative to its containing block. The issue here is that the container block isn't container, so the result looks like:


This is because when a box is absolutely positioned, it is positioned relative to its closest positioned ancestor. Since container is not positioned, inner is positioned relative to the document body. To absolutely position inner relative to container, you need to add position: relative on container. This gives:


IE6: position: absolute div acquiring display: inline

Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            .container { background-color: #eee; border: 2px solid #666; width: 15em; position: relative; }
            .inner     { background-color: #aaa; position: absolute; }
        </style>
    </head>
    <body>
        <div class="container">
            <span>In <em>container</em></span>
            <div class="inner">In <em>inner</em></div>
        </div>
    </body>
</html>

Here the inner block doesn't have a top, right, bottom, or right property. So its position should be the same as if there was no position: absolute. Without the position: absolute, this look like:


And indeed, with Firefox (Safari and Opera), adding the position: absolute produces:


However with IE6 and IE7, this produces:


It is as if IE forgot that the <div class="container"> should be positioned as if it had a display: block, but instead it as if it has a display: inline.  (Adding a display: inline on the container block creates the same effect in Firefox.) A workaround is to add top and left properties on inner:

 
 Firefox
IE6 and IE7
Without top, left


(Same as seen earlier)

 
(Same as seen earlier)
With just top: 1em
 
If we were to use top: 0, inner would be positioned over the text "In container".


In IE6/IE7, this shifts the inner block as same vertical level compared to Firefox. But it still shows at the right of "In container".
 
With top: 1em; left: 0
 
Adding a left: 0 doesn't change anything on Firefox.
 
Adding the left: 0 solves the issue we had previously when we just had top: 1em, and gives the same result on both IE6/IE7 and Firefox.


Absolute positioning below a float

Imagine you want to position a block below some text with absolute positioning. As seen in the previous two sections, for this to work on IE, you need to make sure that the container has a position: relative, and you need to use the top and left CSS properties. But now assume the text in the container element a float: left. (The float isn't particularly useful by itself in this example, but it would be used in a real-life scenario, for instance to position a series of label on the left and a form control on the right of the label.) Your code looks like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
        <style type="text/css">
            .outer {
                width: 20em;
            }
            .container {
                position: relative;
                border-top: solid 2px #666;
                padding: 2px;
            }
            .float {
                width: 10em;
                float: left;
                background-color: #eee;
            }
            .absolute {
                position: absolute;
                top: 1.6em;
                left: 0;
                border: solid 2px #aaa;
            }
        </style>
    </head>
    <body class="yui-skin-sam">
        <div class="outer">
            <div class="container">
                <span class="float">Float</span>
                <div class="absolute">Absolute</div>
            </div>
        </div>
    </body>
</html>


(In this source, the outer div has a width: 20em only limit the length of the border, so it easier to take screenshots that are the same in different browsers. It can be ignored for the purpose of this discussion.)

With Firefox
With IE6/IE7

 
  • The right border of the absolute box is incorrectly positioned to the right of the float box left border.
  • The gray horizontal line at the top of the screenshots only starts at the right of the float.

Those two issues happen in IE because IE does its layout as if container was a box positioned to the right of the float box. The fix for IE is to force the container box to "have layout", for instance by adding zoom: 1 on container:

With Firefox
With IE6/IE7

 

Why does IE behave this way? Quoting from Ingo Chao's excellent article On having layout (footnote are mine):

[...] relatively positioned elements next to floats1 should offset with respect to the padding edge of the parent (i.e. left: 0; on a r.p. element would place it on top of a preceding left floated box). In IE 6, the offset left: value; starts from the right margin edge of the float2, causing a horizontal misalignment by the total outer width of the float.

1 This is true for absolutely positioned elements as well, in this example the div with class absolute.
2 Hence the box with class absolute showing on the right of the floated span, rather below it.

Absolutely positioned box inside a box with overflow: auto or hidden

You have the following situation:
  • A container element (box1) has a overflow: auto or overflow: hidden because you don't want content inside that box to exceed a certain size, and you want to either have scrollbars to access that extra content (overflow: auto) or want the extra content to be cropped (overflow: hidden).
  • You have an absolutely positioned box (box3), i.e. taken out of the flow and displayed on top of other elements. That box will be for instance used to display a contextual menu. You want that box to be positioned relative to the top left of box1.
As seen earlier, you need to add a box around box3 which is positioned, and for this you put a position: relative on box2:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">

            /* Positioning */
            #box1 { overflow: hidden }
            #box2 { position: relative }
            #box3 { position: absolute; top: 1.5em }

            /* Styling */
            #box1 { background: #666; padding: 5px; width: 125px }
            #box2 { background: #999; padding: 5px; width: 100px; height: 50px }
            #box3 { background: #bbb; padding: 5px; width: 75px; height: 75px }
            body  { font-family: sans-serif }

        </style>
    </head>
    <body>
        <br/><br/><br/>
        <div id="box1">1
            <div id="box2">2
                <div id="box3"/>3
            </div>
        </div>
    </body>
</html>

Initial situation

The problem with this is that box3 is cropped by the overflow: hidden.
Removing the position: relative on box2


But that's not what you want, and the whole reason why you put a position: absolute on box3. This behavior is caused by the position: relative on box2. Remove it, and you get the result on the right: box3 now is allowed to escape box1. But now the top: 1.5em is relative to the top of the body, not to top of box1.
Using position: absolute instead of position: relative

The solution is to use position: absolute instead of position: relative on box2. Remember: an absolutely positioned box is positioned relative to its closest positioned ancestor. In general we use position: relative to position a box, but you can use position: absolute as well, which in this case solves your problem.

Absolutely positioned box inside a overflow: auto or hidden positioned box

You have the following situation:
  1. Box 1 has an overflow: hidden or scroll and a position: relative or absolute.
  2. Box 2 has position: absolute, and is for instance used for a contextual menu, and thus should not be clipped by box 1.
This situation is illustrated by the following example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">

            /* Positioning */
            #box1 { overflow: hidden; position: relative }
            #box2 { position: absolute }

            /* Styling */
            #box1 { background: #666; padding: 5px; width: 125px; height: 50px }
            #box2 { background: #999; padding: 5px; width: 100px; height: 50px }
            body  { font-family: sans-serif }

        </style>
    </head>
    <body>
        <div id="box1">1
            <div id="box2">2</div>
        </div>
    </body>
</html>


#box1 { overflow: hidden;
        position: relative }

Because of the position: relative of box 1, the overflow: hidden applies to box 2, even if it has a position: absolute.
#box1 { overflow: hidden }
To confirm this is the case, try removing the position: relative on box 1, and you'll get the expected behavior.

As far as we know, there is no way around this (for instance another box between box 1 and box 2, as done in the previous section). If you are in this situation, and that the position: relative on box 1 isn't really necessary, you could override it with a stronger CSS rule. If it is necessary, you could put the markup of box 2 outside of box 1, but if box 1 has an overflow: scroll, then box 2 won't scroll when the content of box 1 is being scrolled.

As a generalization, to have this cropping effect on box2, the overflow: hidden and position: relative do not need to be on the same element; they could be on nested elements, as in the following example, where the box1a gets the overflow: hidden and box1b the position: relative.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">

            /* Positioning */
            #box1a { overflow: hidden }
            #box1b { position: relative }
            #box2  { position: absolute }

            /* Styling */
            #box1a { background: #666; padding: 5px; width: 125px; height: 50px }
            #box2  { background: #999; padding: 5px; width: 100px; height: 50px }
            body   { font-family: sans-serif }

        </style>
    </head>
    <body>
        <div id="box1a">1
            <div id="box1b">
                <div id="box2">3</div>
            </div>
        </div>
    </body>
</html>

Width property inside an absolutely positioned box

In the following example, you have two absolutely positioned boxes:
  • The first one contains another box with width: 100%.
  • The second one contains an input with width: 100%.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">
            .absolute   { position: absolute }
            .width      { width: 100% }

            /* Styling */
            #box1   { background-color: #eee }
            #box2   { background-color: #ccc }
            #box3   { background-color: #aaa; top: 5em }
            div     { padding: 5px }
            body  { font-family: sans-serif }
        </style>
    </head>
    <body>
        <div id="box1" class="absolute">1
            <div id="box2" class="width">2</div>
        </div>
        <div id="box3" class="absolute">1
            <input class="width" value="Input">
        </div>
    </body>
</html>

What is the impact/meaning of that width: 100%? In both cases:
  • The width of the absolutely positioned depends on the width of its content.
  • The width of the content is set as a percentage, so you make it depend on the width of the absolutely positioned box.
  • So we have a circular dependency, which is "resolved" by CSS 2.1 sayingIf the containing block's width depends on this element's width, then the resulting layout is undefined in CSS 2.1. (This doesn't appear to have changed in a draft of the CSS 3 specification.)
Let's see what browsers do:

Standards-compliant browsers
With standards-compliant browsers (which in this case includes IE8) the width: 100% has no effet; it is as if it wasn't there.
IE6 and IE7 
IE6 and IE7 are less consistent:
  • When the width: 100% is on a div, they ignore it.
  • When it is on an input, they grow the input to what seems to be the width of the viewport.

Aligning an absolutely positioned box in a table cell

Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            .box-2 { position: absolute }

            /* Styling */
            .box-1 { background-color: #ccc; width: 3em  }
            .box-2 { background-color: #ddd; width: 3em  }
            table th, table td { padding: 5px; border: 1px solid #666 }
        </style>
    </head>
    <body>
        <table>
            <tr>
                <td>
                    <div class="box-1">1</div>
                </td>
                <td>
                    <div class="box-2">2</div>
                </td>
            </tr>
        </table>
    </body>
</html>


Initial situation
Notice how box 2 is not vertically aligned with box 1? The position: absolute takes that box out of the normal flow, and since you don't have a top or left, its position should not be affected. It is in this case because being taken out of the flow, it does not take any space anymore, and because the td has a default vertical-align: middle, it top left corner is aligned with the middle of the cell.
Add vertical-align: top on the td

Add vertical-align: top on the td to get box 2 to be vertically aligned with box 1.


Firefox doesn't support position: relative on table cells

Consider you have a box (here box2) with position: absolute inside a table cell (here box1), and you want to position it relative to the td. For this, you put a position: relative on the td, as in:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">

            /* Positioning */
            #box1 { position: relative }
            #box2 { position: absolute; right: 40px }

            /* Styling */
            #box1 { background: #666; padding: 5px; width: 125px; height: 50px;
                    vertical-align: top }
            #box2 { background: #999; padding: 5px; width:  50px; height: 20px }
            body  { font-family: sans-serif }

        </style>
    </head>
    <body>
        <table>
            <tr>
                <td id="box1">1
                    <div id="box2">2</div>
                </td>
            </tr>
        </table>
    </body>
</html>


IE6+
Chrome
Safari
Opera
With Chrome, this works as expected. Because of the right: 40px, the div's right edge is positioned inside the td at 40px from td's right edge.
Firefox
In Firefox however, the position: relative on the td has no effect. As a result, in this example, Firefox positions the right edge of the div at 40px of the right edge of the viewport, which was made quite narrow for the purpose of this screenshot.

The workaround is to add a div inside the td and move the position: relative on that div. This will fix the problem for Firefox, at the cost of adding markup to the page.

In the CSS2 recommendation, we can read:

The effect of 'position:relative' on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.

Since CSS2 says the behavior of a position: relative on a td is undefined, Firefox ignoring the position: relative
technically can't be said to be a bug. However, I want to argue that Firefox should support the position: relative as other browsers do, and that the current behavior in Firefox is a bug:
  1. The spec saying that the behavior is undefined:
    • I can't imagine a use case where not supporting position: relative would be helpful. And obviously we do have a use case where supporting it is useful, avoid the cost of additional markup. So picking the helpful alternative rather than the non-helpful one seems like a better choice.
    • Other browsers choose not to ignore the position: relative in this use case. The spec not mandating a behavior, doing what other browsers do seems like a better choice.
  2. The current working draft of the CSS3 Positioned Layout Module doesn't say that the behavior is undefined for table cells.

Position: relative inside overflow: hidden

Consider you have position: relative inner box inside a overflow: hidden outer box:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">
            .outer { overflow: hidden;
                     width: 100px; height: 50px;
                     background-color: #eee;
                     border: 2px solid #333; }
            .inner { position: relative;
                     width: 70px; height: 50px;
                     background-color: #aaa;
                     border: 2px solid #666;}
        </style>
    </head>
    <body>
        <div class="outer">
            Outer
            <div class="inner">Inner</div>
        </div>
    </body>
</html>

Firefox (and other browsers)
IE6 and IE7

On Firefox and other more standards-compliant browsers, the inner box gets "cut" by the overflow: hidden.
In IE6 and IE7, the overflow: hidden looses its effect when the inner box has a position: relative.

A workaround for this IE bug is to make the outer box relative as well, adding:

.outer { overflow: hidden; position: relative; ...}


Create a block that extends to the bottom of the viewport

You have a page with two blocks: a header and a body. How can you get the body to start below the header, and to extend all the way to the bottom of the viewport, whatever the size of the window is? This situation is illustrated in this screenshot:

One obvious way is to use JavaScript to set the height of the body block at runtime. But you can also do this with CSS: make the body block absolutely positioned, and set both a top and bottom. The trick is there: setting both the top and bottom properties. You can set bottom to a small value, if you want to leave some padding between the body and the bottom of the viewport. The drawback of this technique is that you need to estimate how much vertical space your header needs. If the content of your header is dynamic, this will be a show stopper. Otherwise, it might be an acceptable drawback.

What follows is the full source for the page shown in the above screen shot.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">

            /* Positioning */
            .body   { position: absolute; top: 60px; bottom: 8px; left: 8px; right: 8px }

            /* Styling */
            .header { background: #999; padding: 10px; }
            .body   { background: #ccc; padding: 10px; }
            body    { font-family: sans-serif }
        </style>
    </head>
    <body>
        <div class="header">Header</div>
        <div class="body">Body</div>
    </body>
</html>


IE 7 peek-a-boo with relative box, inside a fieldset, inside an absolute box

Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            /* Positioning */
            .centered { width: 300px; margin: auto }
            .scrollable { width: 300px; overflow: scroll; position: absolute; height: 150px  }
            .relative {  position: relative }

            /* Styling */
            fieldset { background-color: #eee; border: 2px solid #666 }
            .relative { background-color: #aaa }
            body  { font-family: sans-serif }
        </style>
    </head>
    <body>
        <div class="centered">
            <div class="scrollable">
                <br><br><br><br><br><br><br><br><br><br>
                <fieldset>
                    <div class="relative">This text should be entirely visible.</div>
                    <span/>
                </fieldset>
            </div>
        </div>
    </body>
</html>

In this page, you have:
  1. The <div class="centered"> is a box, 300px wide, which is centered. This is fairly typical in a fixed width layout.
  2. Inside, the <div class="scrollable"> defines a scrollable area. This is also is frequent when you want to keep navigational element above or below that area always visible, while the main content of the page is displayed a scrollable <div>. That <div> needs, for some (good) reason, to have a position: absolute.
  3. Inside, the <fieldset> contains a <div class="relative"> which needs, again for some (good) reason, to have a position: relative.
Because there is content before the fieldset, it is not immediately visible when you load the page (here simulated with a number of <br>), and you need to scroll down inside the <div class="scrollable"> to see it. Once you have done so, on IE 7 (not IE 6), you'll see:


To get a result very similar to the one shown in the above screenshot, make the width of your browser to be about the same width of the screenshot. Now, if you slightly resize the window (or do some other action that causes IE to relayout the page), you'll get the expected result, which is:


Yes, this is an IE peek-a-boo issue, but a rather interesting one, as:
  1. This one of those IE 7 specific peek-a-boo issues. It don't happen on IE 6 (or IE 8, the latter being less surprising).
  2. It is as if IE 7 had rendered the relative <div> positioning it without taking into account the top and left offset of the enclosing scrollable <div>.
  3. This is also interesting, because most IE peek-a-boo issues are solved by adding a zoom: 1 on an element (or similar CSS forcing the box to have layout), this one is not!
The culprit here is the fieldset (interestingly, this problem appears to be specific to fieldsets). Fortunately, the solution is simple: make sure your fieldset is relatively positioned, for instance with fieldset { position: relative }.

IE 7 checkboxes display glitch within <ul>

Simplified markup:

<div>
  ...
  <ul>
    <li>
      <span>
        <span>
          <label for="foo·1">
            <input type="checkbox" value="bar" name="foo·1" id="foo·1">
            Cat
          </label>
        </span>
      </span>
    </li>
    ...
  </ul>
  ...
</div>

In this glitch, checkboxes appear completely outside the intended location. Interacting with the boxes puts the checkboxes back where they belong (which is not really a solution).


Solution: make <ul> relatively positioned with ul { position: relative }.


Floats

Prevent a box from extending below a float

The technique for using an overflow: hidden to clear floats, thus preventing you from having to add an element just for the purpose of carrying a clear: both, has been well documented. I first read about it in CSS Mastery: Advanced Web Standards Solutions (published in 2006, page 41). But you can also use an overflow: hidden to prevent a box next to a float from extending below the float. Consider the example (view HTML):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            #box2 { float: left; }
            #box3 { display: block }

            /* Styling */
            #box1 { background: #ddd; padding: .5em .5em; width: 20em }
            #box2 { background: #999; padding: 1em; }
            #box3 { background: #bbb; padding: .5em; }
            body  { font-family: sans-serif }
        </style>
        <script type="text/javascript">
        </script>
    </head>
    <body>
        <div id="box1">
            <span id="box2">2</span>
            <span id="box3">
                Orbeon Forms is a solution to build and deploy web forms.
                Orbeon Forms is a solution to build and deploy web forms.
                Orbeon Forms is a solution to build and deploy web forms.
            </span>
            <span id="box4"/>
        </div>
    </body>
</html>

This is rendered as:


Since box 2 has a float: left, the text in box 3 wraps around it, and the area covered by box 3 goes "underneath" box 2. This is your basic float, but now imagine that you don't want the text in box 3 to wrap around box 2.


Overflow hidden

You can do this by adding a overflow: hidden; zoom: 1 on box 3, which gives you (view HTML):


How it works

Preventing a box from wrapping around a float is not the primary purpose of overflow: hidden; it is merely a side effect. The side effect is that overflow: hidden establishes new block formatting context (BFC), as it is called in CSS. Quoting from the CSS specification: In a block formatting context, each box's left outer edge touches the left  edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats […].

For the overflow: hidden to be effective on IE6 (it does work "out of the box" on IE7+), box 3 needs to have layout, which you can force with zoom: 1.

Drawback

You can't use this technique if either:
  • You have this inside a table with non-fixed-width columns, as with the overflow: hidden, the browser won't consider that your paragraph needs to take any space and might be too aggressive at reduce the width of the column.
  • Box 3 can contain a position: absolute box which needs to be able to overflow out box 3, e.g. a date picker.
Margin left

Another solution is to use a margin-left on box 3, which gives you (view HTML):


Drawback

You can't use this technique if either:
  • The width of box 2 isn't known to you, as you to hard-code it in the CSS. (E.g. this won't be a problem if box 2 is or contains an image.)
  • Box 2 may or may not be present. (E.g. this will be problem if box 2 is an optional icon.)

Sizes

What unit to use for sizing (em, px, %)?

With CSS, you can set size either using:
  • 3 units (em, px, and pt).
  • As a percentage (%).
  • For text, you can use keywords, which can be:
    • Non-relative keywords (xx-small, x-small, small, medium, large, x-large, and xx-large).
    • Relative keywords (smaller and larger).
What should you use when? Here are some rules of thumb:
  1. In most cases, use em when sizing objects on the page – The temptation to use px can be strong, but doing so you might just shoot yourself in the foot. Imagine for instance you have  a button which you size in px to look just right. Later on, the size of the text on that page is changed to be smaller or larger. As a result, your button will look  too small or too large, a problem you wouldn't have had you sized the button in em. Also see the section below on setting sizes on em from IE 6/7 to get the same results on all browsers.

  2. When sizing objects, never mix em or px. If you feel rule 1 doesn't apply to you, and that for some reason you need to use px, use that unit consistently. If you mix px and em, and later on the size of the text is changed, the actual rendered size of those different objects will be different, which is a recipe for disaster. For instance, imagine you have a dialog that contains buttons, and that, in a moment of confusion, you sized the dialog in em and the buttons in px. Later on the size of the text is changed to be smaller. As a result your dialog gets smaller, but the buttons doesn't, and maybe are so big that they even leak outside of the dialog.

  3. For fonts, things get more complicated. Here you could use either px, em, or %:

    • The benefit of em and % is that they do cascade. Imagine you create an object that will be used in a larger page: you set the size of the text in that object to 1em and the size of an excerpt you want to highlight to 1.2em. When you object is placed into a page, the elements will be size relatively to the size of their container: if placed in a page where the text is quite small, the text in your element will be small too, and vice-versa.
    • The downside of em and % is that they do cascade. Imagine you have a section you want to be somewhat larger than normal text, and you size it at 120%. That section contains another section, created by another author who wanted it to be larger than the normal text, and who also put a size of 120%. As a result, the text in the nested section will have a size of 120% x 120% = 144%, instead of the 120% that other author expected.

    em and % are conceptually similar, but the YUI team noted that % are sized more consistently across browsers than em, so in doubt between em and %, go for % and use the specific percentages mentioned in this table to get results that are as close as possible across browsers.

    (Still in doubt about whether you should use px, em or % to size text? OK, then use %.)

Sizes in em for IE 6/7 vs. other browsers

When sizing objects in em, you need to know that an IE6 and IE7 em does not have the same size as it does in other browsers. Since you want your sizes to be the same on all browsers, you need to set a different (smaller) size for IE6/7 than for other browser. The latest suggestion by the YUI team is to use a factor of 13/13.3333. To set a size that is only taken into account by IE6/7, use the CSS star hack. So for instance, if you want to set a width of 30em, the size for IE6/7 will need to be 30*13/13.3333 = 29.25em, and your CSS will look like: width: 30em; *width: 29.25em.


Form elements 

Image not shown in a button on IE7 with Bootstrap

The example below shows as follows in IE7 vs. IE8: the image is missing in IE7.  This happens with Bootstrap, which adds a max-width: 100% on images, and will of course happen in any other case where you're setting a max-width: 100% on images. IE7 seems to think the button has a zero-width, and thus resizes the image to match that contraint (0 * 100% = 0), making it disappear. 

The solution consists in adding CSS that overrides this rule. In the case of Bootstrap, the following will do: button max-width: none.

IE7
 
IE8
 

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            img { max-width: 100% }
        </style>
    </head>
    <body>
        <button>
            <img src="http://placekitten.com/g/100/80" alt=""/>
        </button>
    </body>
</html>


Padding in buttons

This is one of the rare cases where the browser to blame is not IE 6/7, but Firefox 3.6. Consider a button with padding-left: 10em.

Browser Result
Comment
Chrome

Chrome renders this correctly, and just adds 10em to the left of Text inside the button.
Firefox

Firefox 3.6 doesn't, and renders the buttons as if you didn't specify a padding-left.
 


Note that if you add a background image to the button, Firefox does its own rendering of the button instead of using an OS platform, and in that case it honors the padding-left.

You can test this with the following example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            button {
                padding-left: 10em;
            }
        </style>
    </head>
    <body>
        <form>
            <button>Text</button>
        </form>
    </body>
</html>


Buttons and width

The padding on an element is supposed to be "outside" of its width. So if you specify both a width and padding on an element, the actual space it will take on the page will be the width plus the padding. Not so on IE 6/7 for buttons. Consider the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            button, div {
                width: 1em;
                padding-left: 10em;
            }
            /* Styling */
            button, div {
                margin-bottom: 1em;
                border: 1px solid #666;
                background-color: #EEE;
                font-family: sans-serif;
                font-size: 13px;
            }
        </style>
    </head>
    <body>
        <form>
            <button>Text</button>
            <div>Text</div>
        </form>
    </body>
</html>


Browser
Result
Comment
Firefox


Firefox renders this as expected: specifying a padding increases the actual width of the button on the page.
IE 6/7
On IE 6/7, the text is pushed to the left by the padding, but the actual width does not take into account the padding. This bug has been fixed in IE8.


Buttons and text indent

Specifying a text-indent should push the text inside an element; it should not push the element itself. Consider the following example, which is similar to the one in the previous section, with padding-left replaced by text-indent:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            button, div {
                width: 1em;
                text-indent: 10em;
            }
            /* Styling */
            button, div {
                margin-bottom: 1em;
                border: 1px solid #666;
                background-color: #EEE;
                font-family: sans-serif;
                font-size: 13px;
            }
        </style>
    </head>
    <body>
        <form>
            <button>Text</button>
            <div>Text</div>
        </form>
    </body>
</html>


Browser
Result
Comment
Firefox


Firefox renders this as expected: for both the button and the div, the text is pushed to the right by the text-indent, and the width applies to the element as if its content didn't have a text-indent.
IE 6/7

IE 6/7 does the right thing for the div, but in the case of the button, it incorrectly applies the text-indent to the button itself, and in addition applies it to the text inside the button. In essence, this means you can't rely on the text-indent for any practical purpose on IE 6/7. IE8 fixes this problem.


Replacing the text in a button by an image

You inherit of an application or use an engine that generates for you a button with some text. Now imagine that you want an image instead of the text, and want to do so in CSS-only without changing the markup sent to the browser. Assume the markup you are working with is:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            button {
                border: 1px solid #666;
                font-family: sans-serif;
                font-size: 13px;
            }
        </style>
    </head>
    <body>
        <form>
            <button>Text</button>
        </form>
    </body>
</html>

Not surprisingly, this is rendered as:

You want to "replace" the text with an image, to get the following result:

Do this by adding the following CSS:

body button {
    background: url(http://www.orbeon.com/images/orbeon-icon-16.png) no-repeat;
    width: 22px;
    text-indent: -9999px;
    *text-indent: 0;
    *padding-left: 9999px;
}

Let's go over this line by line:
  1. Set the background image (no surprise).
  2. Set the width of the button based on the size of your image (still no surprise).
  3. By specifying text-indent, you get the text to be pushed, here to the left of the button, without influencing the width or position of the button.
  4. As seen earlier, a text-indent pushes the button itself on IE 6/7, instead of pushing the text inside the button. So here you undo the text-indent, using a star hack so only IE 6/7 takes this rule into account.
  5. You are now left with the problem of hiding the text for IE 6/7. You can do so specifying a padding-left. This wouldn't work on other browser, as it would increase the actual width of the button, but as seen earlier IE 6/7 incorrectly interprets the padding-left to only push the text without impacting the actual width of the button, which in this case is quite handy.
If you are doing this for YUI button, in addition add the following to the rule above:

*overflow: hidden;

This overrides the *overflow: visible in YUI's button.css, otherwise anything you have to the left of the button will be pushed by the label of the button you pushed with *padding-left: 9999px.

Vertically aligning an image next to a form control

Imaging you want to place an image next to an input field, and want the center of the image to be vertically aligned with the center of the input control. Here is an example that illustrates this situation:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">
            /* Styling */
            body { font-size: 100%; font-family: sans-serif }
            form { border-top: 1px solid #999; border-bottom: 1px solid #999 }
            img {  border: 1px solid #999 }
        </style>
    </head>
    <body>
        <form action=".">
            Label:
            <input type="text">
            <img src="http://www.orbeon.com/images/orbeon-icon-16.png">
        </form>
    </body>
</html>

The form has a border at the top and bottom, and the image has a border, just so you can better check the alignment of the text, the input control, and the image.


Initial situation

In this initial situation, the image is grossly misaligned with the input control.
Adding: img { vertical-align: middle }
Setting vertical-align: middle on the image, it looks like you are resolving the problem.
Making the font larger
But increase the size of the text, and you'll find that the icon is not exactly aligned with the input field. In this case, with the larger text, the image shows just a bit too high.
Making the font smaller
 
The same is true if you make the text smaller, except in this case the image is a bit too low. By how much the icon is misaligned will depend on the browser. You see this because the input has a baseline vertical alignment while the input has a middle alignment.
Adding: input { vertical-align: middle }

To make the input control and the image aligned at all font sizes, add a vertical-align: middle on the text control.

line-height and vertical-align: middle

Center content vertically inside a box

You can do this without using a table if you know in advance the height of the box. (However, you don't necessarily need to know the height of the content.)  In the example below, the box is a <div> and the content an <img>. This is done, quite simply, with a text-align: center on the box. So the image is gets precisely vertically aligned in the middle of the box, and not on the text's baseline, add a vertical-align: middle on the image.

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            div { line-height: 100px; text-align: center }
            img { vertical-align: middle }

            /* For appearance only */
            div { background: #ccc; width: 300px }
            img { border: 1px solid #999; padding: 3px; background: white }
        </style>
    </head>
    <body>
        <div>
            <img src='http://www.orbeon.com/img/orbeon-logo-trimmed-transparent-42.png'/>
        </div>
    </body>
</html>

line-height has no effect on IE7 when no text is present

On IE7, line-height has no effect if the element it applies to doesn't contain any text. For instance, consider the following example:

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            div { line-height: 100px }
            i   { background-image: url('http://www.orbeon.com/img/orbeon-logo-trimmed-transparent-42.png');
                  width: 212px; height: 42px; display: inline-block; vertical-align: middle }

            /* For appearance only */
            div { background: #ccc; text-align: center; width: 300px; margin-bottom: 10px }
        </style>
    </head>
    <body>
        <div><i></i></div>
    </body>
</html>


On IE8+ and other browsers, the div takes a 100px height, as set with the line-height, and the i element is properly vertically centered inside the div.
However, on IE7, the line-height doesn't have the expected effect.
You can fix this by adding some text inside the div, as shown in the screenshot to the right. Sure, you might say, but I don't want any text? Then adding a zero-width space (&#65279;) will do the trick, as in <div><i></i>&#65279;</div>.

Inline blocks

  • Why use inline block? Amongst other things, inline blocks allow you to vertically align elements you want to display on the same "line". For instance, if you want to align a piece of text (say a label) with a text field or a button.

  • RecipeTo inline a block that looks like:

    <div class="my-inline-block">...</div>

    Use the following CSS:

    .my-inline-block {
        display: -moz-inline-box;
        display: inline-block;
        *zoom: 1;
        *display: inline;
        vertical-align: middle;
    }
  • How it works:

    1. -moz-inline-box: Firefox 3.0 correctly supports inline-block, but earlier versions did not. The -moz-inline-box gives you about the same behavior as inline-block for earlier versions of Firefox which didn't support inline-block.
    2. IE6/7 only supports inline-block for elements that natively inline. Luckily, in IE, elements that have layout and are inline behave as inline block elements. You trigger the element to have layout with *zoom: 1 and make it inline with *display: inline. The star is a CSS hack to ensure only IE sees those properties.
    3. The initial value for vertical-align is baseline. If you want blocks to be vertically aligned on their center, use vertical-align: middle.

  • Further readingThese folks have done some excellent write-ups on the subject:


Overriding CSS rules

The problem:
  • You have the following markup:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>
        <style type="text/css">
            tr { background-color: #9f9; } /* green */
            td { background-color: #9ff; } /* red */
        </style>
    </head>
    <body>
        <table>
            <tr>
                <td>Should be green</td>
            </tr>
        </table>
    </body>
</html>
  • You can't change the CSS, but you can add CSS.
  • You want to override background-color defined on the td (red) so the background-color defined for the tr (green) applies.

 CSS Firefox and other "good" browsers
With IE6 and IE7
body td { 
    background-color: inherit;
}
Works as expected.


Unfortunately, inherit is not supported by IE6 and IE7.


body td {
    background-color: transparent;
}
Works as expected.

The transparent property is recognized by IE, but both IE6 and IE7, but they don't know how to apply the color defined for the tr on the cell.

body td {
    background-color: #9f9;
}
Works as expected.

This works, but is unfortunate as it forces you to say "apply a green background color for this td" instead of "don't apply the default background color for this td".

So you have to copy the color definition, which means that you will need to update your stylesheet if the tr background color changes in stylesheet in which you want to override the definition for the td background color.

IE: missing borders

IE has a tendency to cut borders on boxes in a whole range of situations. For instance, consider this ultra-simple case with a simple span with a border:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style type="text/css">

            /* Box alignment and border */
            .box { border: 1px solid #666; }
            /* Styling */
            body  { font-family: sans-serif }
        </style>
    </head>
    <body>
        <span class="box">Gaga</span>
    </body>
</html>


On Firefox

Firefox, and "all" the other browsers, including IE8, render this just as you would expect.
On IE6/7
However, IE 6/7 doesn't render properly the borders at the top or bottom of the span.
Solution

A solution in this case consists in making the span behave in "like" an inline-block, by adding the following CSS, which we only want to be active for IE 6/7, hence the stars:

*display: inline;
*zoom: 1;

As mentioned earlier, the "missing border" symptom happens in a number of cases. Sometimes, the solution will consist in adding a margin, just for IE, around the box that has the border, or around a container to that section.

IE6 to IE9 limit of 31 CSS files

IE9 and earlier won't load more than 31 CSS files. This limit has been lifted with IE10. I have seen mentions of that limit being 32 instead of 31; while 32 would make more sense, IE hasn't been able to load more than 31 files in my experience. Inlining CSS with a <style> counts as a "file", so to be more precise we should say that IE will only take into account the first 31 <link rel="stylesheet"> or <style> in your HTML document. While this isn't (or shouldn't be!) a problem for deployed applications, it can be in development, when your CSS isn't combined.

Firefox: vertical-align: baseline stops working when using overflow: hidden

On Firefox, the vertical-align: baseline stops working for a box on which you have an overflow: hidden. You can see this in the following set of screenshots. The box in question here has a gray background. On Firefox, without the overflow: hidden, the text inside the gray box is at the same level as "Label:" but then looses its allignement when adding an overflow:hidden. Chrome/Safari doesn't have this problem.


You can reproduce this by running this jsFiddle or with the following:
  • CSS:

    .block {
        width: 10em; padding: .3em 0 .1em 0;
        overflow: hidden;
        white-space: nowrap;
        display: inline-block;
        border: 1px solid #666; background-color: #eee;
    }

  • HTML:

    <span class="label">Label:</span>
    <span class="block">Block with content, which can be quite long.</span>

This has been filed as bug 615712 with Mozilla. There is no satisfactory workaround, but as a gap measure, you can either:
  • Set the box to vertical-align: middle. This is obviously is not the same as baseline alignment, but in most cases will improvement the alignment.
  • The Photoshop guy solution: moving the box by a certain number of pixels down, until it looks good (with the current font face, font size, current zoom level…), for instance: block { position: relative; top: 7px }. Then you'll need to either on the server-side conditionally serve this CSS only to Firefox, or on the client-side inject it with JavaScript if the browser is Firefox. Ugly.

HTML and the DOM

Specifications


Get elements with prefixes

To deal with elements in a namespace, DOM Level 2 added getElementsByTagNameNS. However that method is only supported by IE9+. Also, Chrome (as well as other WebKit-based browsers, including Safari)  implemented the non-namespace-aware version, getElementsByTagName, differently than other browsers: they expect a local name, while other browsers expect a name with a prefix. Fortunately, using jQuery $(element).children('prefix\\:localName') works in all the browsers. This state of affair is summarized in the table below, and you can test this by running this jsFiddle.

Using jQuery's children()  will only work if you're looking for the direct children of an element, and not for descendants that might be nested deeply. jQuery's find() is a closer equivalent to getElementsByTagName(), in the sense that it goes through all the descendants, but just like getElementsByTagName(), it behaves differently across browsers.

 ChromeFirefox 3+IE7IE8IE9IE10IE11
getElementsByTagName('child')YesNoNoNoNoNoNo
getElementsByTagName('xxf:child')NoYesYesYesYesYesYes
getElementsByTagNameNSYesYesNoNoYesYesYes
jQuery's .children('prefix\\:localName')YesYesYesYesYesYesYes

Which elements are focusable?

Wether an element is focusable has a number of implications. For instance, if an element is focusable:
  • When you click on it, it receives focus events (e.g. focusin).
  • Then document.activeElement points to that element.
  • When you click on an other element (whether that other element is focusable or not), the elements gets blur events (e.g. focusout).
According to the section of HTML5 on focus management, browsers have some latitude to decide what elements are focusable, in particular to conform with OS conventions, but as a rule of thumb form elements and links should be focusable. In addition, on IE, it seems that any visible element is focusable. So for instance, a div is focusable. You can test this running ie-span-focus.html.

Assume that in a section of a page you have an input field. If you listen to focusin events on that section of the page, because of IE, you can't make the assumption that when you receive that event the input got the focus; it could also be that users clicked on other content in that section, and that those elements (span, div…) received the focus.

Multiple selection lists, value change, and focus event on Firefox

The multiple selection list allows you to both change the focus to that control and make a selection with one action: clicking on an item in the list. When the happen, the focus events are dispatched first (focusin and focus), followed by the change event. If you were to read the value during one of the focus events, would you get the new or old value? With Firefox, you get the new value; with all the other browsers, you get the old value:

Browser Value change respective to focus or focusin
 Chrome 19 After 
 Firefox 11 Before
 IE 6, 7, 8, 9 After 
 Opera 11 After 

You can reproduce this with the following example:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <link href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" rel="stylesheet">
        <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.js">
        </script>
        <script type="text/javascript">

            $(function() {
                function log(event) {
                    $('ol').append($('<li>').html(event + '; value=' + $('select').val()));
                }
                $('select')
                    .on('focus',   function() { log('focus'); })
                    .on('focusin', function() { log('focusin'); })
                    .on('change' , function() { log('change'); });
            });
        </script>
    </head>
    <body>
        <div class="container">
            <div class="page-header">
                <h1>Multiple selection lists, value change, and focus event on Firefox</h1>
            </div>
            <div class="row">
                <div class="span4">
                    <p>
                        Click on "2" in the list and watch the events
                        being dispatched by your browser.
                    </p>
                </div>
                <div class="span4">
                    <form action="/">
                        <select multiple id="my-select">
                            <option value="1" selected>1</option>
                            <option value="2">2</option>
                        </select>
                    </form>
                </div>
                <div class="span4">
                    <ol id="out"></ol>
                </div>
            </div>
        </div>
    </body>
</html>


On Firefox, running the example you get:



Closing </script> tag

Unless the web page you serve to the browser is interpreted as XHTML (and not HTML), you need to have a closing </script> tag. That is:
  • If your page is interpreted as XHTML, you can write:
    <script src="my-script.js"/>
  • If your page is interpreted as HTML, you need to write:
    <script src="my-script.js"></script>
To indicate that you are serving XHTML, you will want to:
  1. Specify an XHTML doctype, for instance:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. Specify an XML Content-Type in the HTTP headers (e.g. application/xml instead of text/html).
How strictly those requirements are applied will depend on the browser. For instance, just #1 is enough on Safari 3.1 and Opera 9, but not on Firefox 3.0 (the later is a bit surprising).

In IE, nodes become disconnected when removed from the DOM with an innerHTML

Assume you have a DOM with a <div> that contains a <p> that contains an <i>. In JavaScript, first get a reference to <p> and <i>. Now consider the the following two scenarios:
  1. You detach <p> from its parent. At that point, the <div> is empty, but you still hold a reference to the <p>. And that <p>, which at this point is detached from the document, still has <i> as its child, and <i> has <p> as its parent. All is good.
  2. You replace the content of the <div> with innerHTML. From the perspective of <p> nothing changes: <p> gets detached from the DOM. But now, on IE (and only on IE), your reference to <p> doesn't have any children, and <i> doesn't have a parent. It is as if, when nodes are detached from the DOM through an innerHTML, they get disconnected from each other. Could this have been an ancient strateg to avoid memory leaks? Whatever the reason is, it looks like a bug, which I reproduced on all the versions of IE I tested this on, including IE10. The workaround is simply to detach <p> before you use innerHTML on <div>.
You can reproduce this with the following example.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
        <script>
            $(function() {
                var div = $('div');
                var p = $('p');
                var i = $('i');

                function log(situation) {
                    console.log(situation + ' p has a child: ', p.children().is('*'));
                    console.log(situation + ' i has a parent: ', i.parent().is('*'));
                }

                p.detach();
                log('After detach');
                div.append(p);

                div.html('New');
                log('After innerHTML');
            });
        </script>
    </head>
    <body>
        <div>
            <p>
                <i>Original</i>
            </p>
        </div>
    </body>
</html>

Change event on Firefox 2 for Mac OS X

Firefox 2 had a bug on OS X (and not on Windows) with the change event. It happened in the following scenario:
  1. Change the value of a form field.
  2. Ctrl-tab out of Firefox.
  3. Ctrl-tab back to Firefox.
  4. Changes the focus to another field.
At this point, Firefox doesn't dispatch the change event to the form field. This forced us to have code running on blur for that control or focus for another control and that would detect the change. This bug has been fixed in Firefox 3, which was released on June 17, 2008. Since then, most Firefox users have upgraded to Firefox 3, so we phased out this work-around as of June 10, 2009.

Change event for typed inputs on iOS

iOS 5 supports a number of date/time types on input controls; instead of type="text", you can use type="date", type="datetime", type="month", and type="time". However, Mobile Safari on iOS 5 doesn't fire the change event when the value of a, say, type="date" field is changed and the field looses the focus, unlike what happens with an input type="text"This issue is fixed in iOS 6. You can reproduce this with the following test case.

If you want to be informed when users change the value of "typed" input field on iOS, it looks like you'll need to listen on the blur event instead of the change event.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <script type="text/javascript"
        src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>
        <script type='text/javascript' src='http://code.jquery.com/jquery-1.7.js'></script>
        <script type="text/coffeescript">
            $('input').on "change blur", (event) ->
                console.log event.type, event.target.id
        </script>
    </head>
    <body>
        <form>
            <input type="date" id="date">
            <input type="text" id="text">
        </form>
    </body>
</html>

Getting the value of custom attributes with IE

With IE, up to IE8, you could get the value of non-HTML attributes through a property. For instance, if you had a <div id="myDiv" gaga="42">, you could write document.getElementById('myDiv').gaga. This is not the case anymore with IE9 onwards. You can check this with the following example.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
        <script>
            $(function() {
                var div = $('div');
                console.log('Reading with .gaga: ', div.get(0).gaga);
                console.log('Reading with .attr(): ', div.attr('gaga'));
            });
        </script>
    </head>
    <body>
        <div gaga="42"></div>
    </body>
</html>

getAttribute

When an attribute is missing on an element, Firefox and other more standard-compliant browsers return null, while IE6 and IE7 return empty string. You can see this by running the following example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo/yahoo-min.js"></script>
        <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/event/event-min.js" ></script>
        <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/dom/dom-min.js" ></script>
        <script type="text/javascript">
            YAHOO.util.Event.onDOMReady(function() {
                var div = YAHOO.util.Dom.get("my-div")
                div.innerHTML += "Lang attribute on root element is null: ";
                div.innerHTML += YAHOO.util.Dom.getAttribute(document.documentElement, "lang") === null;
                div.innerHTML += "<br/>Lang attribute on root element is empty string: ";
                div.innerHTML += YAHOO.util.Dom.getAttribute(document.documentElement, "lang") === "";
                div.innerHTML += "<br/>Style attribute on div is null: ";
                div.innerHTML += YAHOO.util.Dom.getAttribute(YAHOO.util.Dom.get("my-div"), "title") === null;
                div.innerHTML += "<br/>Style attribute on div is empty string: ";
                div.innerHTML += YAHOO.util.Dom.getAttribute(YAHOO.util.Dom.get("my-div"), "title") === "";
            });
        </script>
    </head>
    <body>
        <div id="my-div"/>
    </body>
</html>

Setting the value of number attributes

If a DOM attribute is of type number, say colSpan on a <td>, then setting myTd.colSpan = "something" causes an "Invalid argument" error on IE, but not on other browsers. (This has been tested on IE 7 and IE 8.) You can test this on this example (source is also attached). Surprisingly jQuery doesn't smooth this difference between browser, and you'll get the same error on IE with myTd.attr("colspan", "something").

Preserving the DOM on browser back

When you navigate away from a page and then later come back:
  1. In some cases, modern browsers keep the DOM of the page around: changes you might have done to the DOM will be kept, and event dispatched on page load, such as DOMContentLoaded and load are not dispatched.
  2. In other cases, browsers recreate the page, just like they did the first time. (In this case, the markup and other resources might comes from the browser cache or might be reloaded from the web server; this distinction doesn't matter for this discussion.)
You can test if a given browser keeps the DOM on back with the following HTML below (view online). Load it, click on the Remove me link, which removes it form the DOM, click on the Orbeon link, and when that page loaded click the browser's back button. If the browser is all all able to keep the DOM, you'll get the page without the Remove me link.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">
            body { font-family: sans-serif }
            div { font-size: xx-small }
            a { display: block }
        </style>
    </head>
    <body>
        <a id="to-remove" onclick="this.parentNode.removeChild(this);
                                   return false;" href="#">Remove me</a>

        <a href="http://www.orbeon.com/">Orbeon</a>
    </body>
</html>


Browser
Can restore DOM
Firefox 4
Yes
Chrome 12
Yes1
Safari 5
Yes
Opera 11.10
Yes
IE 6
No
IE 7
No
IE 8
No
IE 9
No

1 Chrome 12 doesn't restore the DOM if the developer tools are open. Neither Safari 5 or Firefox 4 (with Firebug open) exhibit the same behavior.

In general, getting the browser to restore the DOM, when it is able to do so, is more desirable: the page will be in the state users left it without you having to do any work, and it will show faster. So what can you do to be in this case? Simple: don't register a listener on unload. If you already don't, check if any library you're using does; for instance, jQuery was reported to do so before version 1.4.

While having an unload handler is for sure a killer if you want the browser to keep the DOM, not having any unload handler is not always enough. For instance, browsers never seem to preserve the DOM for pages generated by Orbeon Forms (even pages as simple as Hello World), even after removing all the unload handlers. As of this writing (2011-04-14), we have not yet figured out why browsers are never able to preserve the DOM for those pages.

Restoring form fields on back

When you navigate back to a page you previously visited, the browser might restore the DOM as it was when you left the page. When it doesn't, it will recreate the DOM, but will still try to restore the value of form fields to their value just before you left the page. Interestingly WebKit-based browsers don't seem to restore the value of hidden input fields, while Firefox and IE do. (As of 2011-04-13, this is the case with Chrome 12 and Safari 5.0.4.) You can check how your browser behaves with the following test case (view online):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <style type="text/css">
            body { font-family: sans-serif }
            a { display: block }
            form { display: none }
        </style>
        <script type="text/javascript">
            var myText, myHidden;
            window.onload = function() {
                myText = document.getElementById("my-text");
                myHidden = document.getElementById("my-hidden");
            }
        </script>
    </head>
    <body>
        <a onclick="myText.value = myHidden.value = 'dynamically set';
            return false" href="#">Set field values</a>
        <a onclick="this.innerHTML = 'Text: ' + myText.value + '. Hidden: '
            + myHidden.value + '.'; return false" href="#">Read field values</a>
        <a href="http://www.orbeon.com/">Orbeon</a>
        <form action="http://www.orbeon.com/">
            <input type="text" id="my-text" value="initial">
            <input type="hidden" id="my-hidden" value="initial">
            <button id="set-values">Set values</button>
        </form>
    </body>
</html>


Browser
Restore text field
Restores hidden field
Firefox 4
Yes
Yes
Chrome 12
Yes No
Safari 5
Yes
 Yes
Opera 11.10
Yes
 Yes
IE 6
Yes
 Yes
IE 7
Yes  Yes
IE 8
Yes Yes
IE 9
Yes  Yes

If you are dynamically setting hidden form fields, storing state you would like to retrieve when you come back to the page, an alternative is to use the HTML5 history API. Instead of storing a value in a hidden field, you use history.replaceState(). Then on page load, instead of reading the value of the hidden field, you register a listener on the window.onpopstate event. Your handler receives an event, and event.state contains the state you previously saved. However this approach has two downsides:
  1. window.onpopstate is dispatched after window.onload, which itself comes after DOMContentLoaded, the event on which you usually initialize your page. This means that in your code running on DOMContentLoaded, you can't know yet if you will later need to restore some state, so you might do some work there which isn't needed in the cases where you need to restore state. Also, you will have to wait longer (until after window.onload) to start restoring the state.
  2. Since IE (even IE 9), does not support the HTML5 history API, you still need to keep, for IE, a version of your code that uses a hidden input field. Having two completely different implementations of a functionality, each one used for different browsers, leads to more browser-dependent bugs and more code to maintain.
For those reasons, in Orbeon Forms, we got around this problem by using a text field (instead of hidden field), and hiding it with a display: none.

JavaScript

Prototypes and constructors

When you declare a function, out of the box, it has a property prototype which points to an object. That object is hence called prototype, and is specific to your function. It comes with a constructor property pointing back to your function:





Objects are created by using the new operator on a function, for instance: new PintGlass(). When you use a function this way, the function is often called constructor. But keep in mind that this is just a way of naming some functions based on their intended used. Constructors are just regular functions. The new object that gets created has a reference (not a copy) to the function's prototype. On Firefox, you can access the prototype using the __proto__ property, but that reference, even if not programmatically accessible, is there in any JavaScript engine:




Note the difference between a function's prototype and an object's prototype: in the case of the function, you access the prototype with the prototype property; in the case of the object, you generally don't access the prototype (you can on Firefox with the __proto__ property) but the object has a reference to the prototype. Object's prototype drive how object's properties are found. Consider:

function PintGlass() {}
PintGlass.prototype.fillWithBeer = function() {};
var myGlass = new PintGlass();
myGlass.owner = "Me!";

This creates:



When you access an object property, that property is first looked up in the object itself. If not found, it is looked up in the object's prototype. If still not found there, it is lookup in the object's prototype's prototype, and so on, until an object's prototype is null. So in the above case, you can write:
  • myClass.owner – This will directly return the owner property of the myGlass object.
  • myClass.fillWithBeer – Since myGlass doesn't directly have a fillWithBeer property, JavaScript looks it up in the object's prototype (shown as P above). That object does have a fillWithBeer property (pointing to a function), and so its is returned.
You can use the hasOwnProperty() method to check if a property is directly available on an object, or if it is rather obtained through its prototype. In the above case:

myGlass.hasOwnProperty("owner");         // true
myGlass.hasOwnProperty("fillWithBeer");  // false

A prototype chain always ends with Object, the root object:




Build your JavaScript classes so the objects have meaningful constructor

Lots of JavaScript classes are declared this way:

function PintGlass() {}
PintGlass.prototype = {
    fillWithBeer: function () {},
    drinkBottomUp: function() {}
};

A negative consequence of building classes this way is that the constructor property on your instances of PintGlass won't point back to the PintGlass function:

var myGlass = new PintGlass();
myGlass.constructor == PintGlass; //false

TBD

Setting the focus on IE

IE has at least two issues that relate to setting the focus:
  1. As mentioned in the jQuery documentation for .focus()attempting to set focus to a hidden element causes an error in Internet Explorer. Take care to only use .focus() on elements that are visible. Here "hidden" means visibility: hidden. Instead, setting the focus on a control with display: none just has no effect, and doesn't cause an error. This problem is solved in IE9.
  2. In addition, even if the element you're trying to set the focus on is visible, focus() might not fail, but just have no effect if the element used to have the focus, but just lost it because you moved it to a new location in the DOM. On IE9 this second issue is mostly fixed; in some cases however, a straight focus() will set the focus to the input field, but IE9 doesn't might not show a blinking cursor in the control, assuming it is an input.
You can reproduce this by running the attached ie-focus.html example, shown in the following screenshot. You can click on the first button Focus on hidden input to reproduce the error described above on IE. Click on the second button Focus on moved input to reproduce the second issue. If you change the radio button to Custom, the code will workaround to this IE issue, and described below.



To work around the second problem described earlier, first introduce a utility function which sets the focus on an element an resolves a differed object when done. The resolution happens right away on standard browsers, but only happens later on IE when the focus really takes effect. This code verifies that the element got the focus by checking that document.activeElement point to the element it sets the focus to.

function setFocus(element) {
    var deferred, setFocusWorker;
    deferred = $.Deferred();
    (setFocusWorker = function () {
        element.focus();
        var focusSet = document.activeElement === element[0];
        focusSet ? deferred.resolve(): _.defer(setFocusWorker);
    })();
    return deferred;
}

Next, use this function to the focus on another focusable element, before setting it again on the input field:

setFocus(focusable).then(function() { setFocus(input); });

Optional groups in regular expressions

Consider the snipped below. It defines a regular expression with an optional group (^(a)?$), and applies the expression to an empty string. The empty string matches the regular expression, and the question is: what does the array contain for the part of the regular expression corresponding to the optional string?
  • IE6 and IE7 return an empty string.
  • Firefox 3, Safari 4, and Opera 9 return undefined.
So be careful in your code when checking what is returned by a regular expression with an optional group, as some browsers return an empty string while others return undefined.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <script type="text/javascript">
            window.onload = function() {
                var re = /^(a)?$/;
                var bits = re.exec("");
                document.getElementById("result").innerHTML = typeof bits[1];
            };
        </script>
    </head>
    <body>
        <div id="result"/>
    </body>
</html>

Chrome's DOM Exception 8

This is a Chrome-only error, which, I assume, can happen in a number of cases. One case is when you are trying to do a DOM mutation while another DOM mutation is happening, and that the former conflicts with the latter. One way to reproduce this is to set the focus on an input, and have a listener move the input on blur. Now imagine that for another reason, the input is moved in the DOM. As the move operation happens, the input is removed from its container, so it looses the focus, which runs the move operation, this while the input is being moved. And you guessed it: Chrome will serve you a DOM Exception 8This isn't a problem with Firefox or IE. With IE, this can't be an issue, as IE differs dispatching focus and blur events.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            $(function() {
                var input = $('input');
                function moveInput() { $('#to').append(input); }
                input.focus();
                input.blur(moveInput);
                moveInput();
            })
        </script>
    </head>
    <body>
        <div id="from"><input type="text"></div>
        <div id="to"></div>
    </body>
</html>


IE form element name/id mixup

Some versions of IE have a double nasty bug:
  1. document.getElementById() returns the first element not with the id requested, but with a matching name attribute
  2. dynamically setting the name of a form control does not update IE's internal name => control map

This is particularly annoying when cloning repeat templates. Say:

<span id="my-input">
  <input id="my-input$$c" name="my-input">
  • getElementById("my-input") correctly returns <span id="my-input">
  • clone the template, including update name attribute on the cloned control from my-input to my-input·1
  • IE updates the name, but does not update its map
  • getElementById("my-input") now incorrectly returns <input id="my-input$$c·1" name="my-input·1">
  • that's because IE mixes up the element id and the name, AND the name my-input incorrectly points to the cloned element
This behavior seems fixed in IE 8 and up in standards mode, but not in quirks mode.

One way to fix this is to recreate the form element with the name attribute:

var clone = document.createElement("<" + element.tagName + " name='" + newName + "'>");

As of 2010-09-22, when cloning repeat templates:
  • We use this method for <input type="radio"> only, as radio buttons groups would be broken otherwise.
  • We do not use it for <textarea> and for all other <input> types, as this might have a negative performance impact. Since our code does not request form elements within templates by id or name, at this point it is an acceptable solution.

YUI

JavaScript error when creating a YUI Panel with ARIA

Consider the following example which creates a YUI Panel dynamically (not from existing markup):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title>JavaScript with YUI panel and ARIA</title>
        <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.8.1/build/container/assets/skins/sam/container.css" />
        <script src="http://yui.yahooapis.com/2.8.1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
        <script src="http://yui.yahooapis.com/2.8.1/build/container/container-min.js"></script>
        <script src="http://developer.yahoo.com/yui/examples/container/assets/containerariaplugin.js"></script>
        <script type="text/javascript">
            YAHOO.util.Event.onDOMReady(function() {
                var myPanel = new YAHOO.widget.Panel("my-panel");
                myPanel.setBody("Hey there");
                myPanel.render(document.body);
                myPanel.show();
            });
        </script>
    </head>
    <body class="yui-skin-sam">
    </body>
</html>

This leads to a error in YUI containerariaplugin.js in the following code:

configRole: function (type, args) {
    [...]
    if (sRole) {
        switch (sRole) {
            case _ALERT_DIALOG:
                oBody = this.body;
                sID = oBody.id || Dom.generateId(oBody);
                this.cfg.setProperty(_DESCRIBED_BY, sID);
            break;
            case _DIALOG:
                oHeader = this.header;
                sID = oHeader.id || Dom.generateId(oHeader);
                this.cfg.setProperty(_LABELLED_BY, sID);
            break;
        }
        [...]
    }
}

If the role is dialog, which is the default, YUI attempts to get the header of the panel, which in this case fails as this panel just has a body. A workaround is to explicitly tell YUI that the role of the dialog is alertdialog with new YAHOO.widget.Panel("my-panel", { role: "alertdialog"}). The downside is that you might have to change a lot of your code.

Another workaround is to change YUI code. YUI adds aria-describedby when the role is alertdialog and aria-labelledby when the role is dialog. Instead, it could add both, conditionally for the header. In addition to fixing the JavaScript error, a benefit (verified with JAWS), is that the screen reader will read the title of the dialog in addition to its content.

if (sRole) {

    oBody = this.body;
    sID = oBody.id || Dom.generateId(oBody);
    this.cfg.setProperty(_DESCRIBED_BY, sID);

    oHeader = this.header;
    if (oHeader != null) {
        sID = oHeader.id || Dom.generateId(oHeader);
        this.cfg.setProperty(_LABELLED_BY, sID);
    }

    setARIARole(this.innerElement, sRole);
    setRoleForCloseButton.call(this);
}

A ticket has been filed with YUI for this.

jQuery Mobile

Change events swallowed

Consider the following example with two radio buttons. We would like to register an event listener on the first radio button for the change event. Without jQuery Mobile, you can do so with the DOM's addEventListener() API, but when using jQuery Mobile the listener isn't called. But it is, "by magic", when you register your event listener with jQuery.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="stylesheet"
            href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
        <script src="http://code.jquery.com/jquery-1.7.1.js"></script>
        <script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js">
        </script>
        <script type="text/javascript">
            $(function() {
                // Not called
                document.getElementById('true').addEventListener("change", function() {
                    console.log("change with DOM API"); });
                
// Called, by magic  
                $('#true').on('change', function() {
                    console.log('change with jQuery API'); });                                     
            });
        </script>
    </head>
    <body>
        <form>
            <input type="radio" name="choice" value="true" id="true"/>
            <label for="true">Yes</label>
            <input type="radio" name="choice" value="false" id="false"/>
            <label for="false">No</label>
        </form>
    </body>
</html>


Browser hangs

This section describes different scenarios which cause a browser hang. We call browser hang situations where the browser stops responding to users' input, often using 100% of the CPU. Browser hangs are especially hard to debug, as when they happen tools won't be able to talk to the browser anymore. IE is particularly vulnerable to browser hangs.

IE: Calling focus() inside an iframe

Calling focus() on form fields of a page running inside an iframe can get IE to hang.

This can happen fairly easily in the Orbeon Forms client-side unit tests, as when unit tests are ran using the YUI Test Manager, each unit test is executed inside a iframe. For unit tests, a workaround consists in directly calling the code that listens on the focus event rather than calling myInput.focus():

ORBEON.xforms.Events.focus({ target: myInput });