The mysterious WebKit placeholder overflow bug

A couple of projects I’ve been working on lately have triggered a frustrating overflow bug that took me ages to find the cause of. Sometimes a horizontal scrollbar would appear for no obvious reason.

I first noticed it in narrow mobile viewports when testing changing the orientation from landscape to portrait in the iOS Simulator, which made me think that it happened only in iOS WebKit. However when I made a minimal test case to try to isolate the problem it turned out that it happens in WebKit-based desktop browsers like Safari, Chrome, and iCab as well. I haven’t been able to reproduce it in any other browsers though.

To see the problem, open up the WebKit placeholder overflow bug demo page in a WebKit browser (verified in the latest versions at the time of writing of Safari/Chrome/iCab on OS X 10.8, Safari on iOS 6 and Chrome on Ubuntu 12.10 and Windows 7). On the page you will find several variants of this HTML snippet:

<div class="container" id="container-1">
    <input type="text" id="textfield-1" placeholder="Dummy placeholder text" />
</div>

The associated CSS, simplified:

#container-1 {
    float:right;
}
#container-1.active {
    float:none;
}
#container-1 input {
    box-sizing:border-box;
    width:100%;
}

So, a text input with a width of 100% inside an element that is floated right, shrinkwrapping to the input element’s intrinsic width. To trigger the bug, either give the input field focus by tabbing to it or clicking in it. This will trigger a script that toggles a class name on the containing element, changing float:right to float:none. I also added a media query to emulate this behaviour when changing orientation from portrait to landscape without having to put focus in the text field. In both cases #container and #textfield will be as wide as the parent of #container. So far, so good.

Next, remove focus from the input field. The script then removes the active class name from #container so that it will be floated right again. The CSS in the media query does the same thing when you change orientation from landscape to portrait. As expected, the width of the input field changes back to the original, intrinsic width. Again, so far so good.

But hey, where did that horizontal scrollbar come from?

After a while I came to the conclusion that it was somehow related to toggling the alignment/positioning of a text input from right to left and back. The same behaviour is triggered if you remove all float declarations and use text-align:right and text-align:left instead (see the demo page for examples of this). It even happens if you use position:absolute to put the containing elements on the right side of the viewport.

For some reason it seems that the WebKit rendering engine has trouble recalculating the input field parent’s width properly when the input field’s width is changed and it appears at the right side of its parent (I also tried left-aligning/floating in an rtl document, which did not cause the problem).

But what causes this? Well, I accidentally removed the placeholder attribute from the input field. And the bug disappeared. Resizing, changing orientation, focusing and blurring, nothing would trigger it anymore. That was a bit of a surprise, to say the least. I put the placeholder attribute back and gave it a null value (placeholder=""). Same thing – problem gone.

Ok, so now we know what causes this strange bug, but is there a workaround? Yes, there are a few options:

  • Use overflow:hidden on the container element. This is probably the most attractive fix to most people as you can keep the placeholder attribute. It won’t work in every situation though as overflow:hidden may have unwanted side effects like cutting off the input field’s focus indicator.
  • Don’t use the placeholder attribute. It isn’t required after all, and all input fields need a proper label anyway. And the Web did survive without it for fifteen years or so.
  • Use JavaScript to remove the placeholder attribute (or set its value to an empty string) on focus and restore it on blur. A bit hacky, but doable if neither of the previous options work for you.

Finally, in case this isn’t actually a WebKit bug but the expected behaviour, I’d love an explanation :-).

Posted on January 12, 2013 in Browsers, CSS

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.