Thursday, October 15, 2009

Script elements with height (and scrollbar issues)

I've encountered this situations a couple of times, what happends is that, at the end of the body of the document or of a major element like a form or a page wide div, sometimes (I haven't really been able to reproduce it at will) hidden elements start having height. This includes hidden divs and spans and even script elements. It seems to be happening mostly on Internet Explorer, but the behaviour has reportedly also been found on Opera and even Chrome.

For a specific example, I had this page with a lot of script tags at the end of the form element. Curiously, only when some spans with display:none were added at the end of the body element the problem would become evident as the height of the page would increase by 12px and a vertical scrollbar would appear. I also encountered the issue when I moved, via script, the calendar popup div for a datetime editor at the end of the body element to solve a positioning issue. Both were happening in "quirks mode" because I am forced to use a HTML 4.01 Transitional doctype.

To me it seems that the browser considers that script tags have no layout, so there is no problem to put them all one above the other in that extra 12px bit, but it does consider it needs to reserve that 12px space. Well, does it have layout or doesn't it?

I looked on the web for people with similar problems and I have encountered very few that actually tackled it. Here is one example of a solution from the Perishable Press blog: Prevent JavaScript Elements from Breaking Page Layout when Following Yahoo Performance Tip #6: Place Scripts at the Bottom.

For short, the solution in the above link is to place all the scripts at the end of the page, but inside a hidden div element. If you have access to all the layout, that is a great solution. However, I found that I could not use it, since the scripts were not added by me and I had no control over their placements. So I found an alternative solution, after noticing that the extra size would dissappear if the popup div of a control was shown.

Well, my solution is quite obvious: if there are issues with hidden and script elements placed at the end of the page, then why not try to add an element at the end of the page and thus make hidden elements NOT be at the end? :)

A short jQuery script did it for me:
$('script').each(function() { 
if ($(this).height()>0 && !$('form').children(':last').is('div.scrollFix'))
$(this).parent().append('<div style=\'position:absolute;background:transparent;width:0px;height:0px;line-height:0px;font-size:1px;\' class=\'scrollFix\' />');
or, in normal Javascript:
var scripts=document.getElementsByTagName('script');
for (var i=0; i<scripts.length; i++) {
var script=scripts[i];
var parent=script.parentNode;
var lastChild;
if (parent&&parent.childNodes.length>0)
var isFixed=(lastChild&&lastChild.tagName&&lastChild.tagName.toLowerCase()=='div'&&lastChild.className=='scrollFix');
if (script.offsetHeight>0&&!isFixed) {
var div=document.createElement('<div style=\'position:absolute;background:transparent;width:0px;height:0px;line-height:0px;font-size:1px;\' class=\'scrollFix\' />');

The script is improved by actually scanning the last element of the parent element for the hidden div so that it doesn't add a zillion divs, one for each script tag. However, it doesn't cover the situation where other elements, rather than the script elements, cause the problem. However, the principle remains sound.