Accéder au contenu principal

HTML tables and auto-sizing cell heights : a workaround

Although this was common practice in the early days of the Web, working with HTML tables when it comes to building a webpage layout should be avoided as much as possible nowadays. CSS offers all the tools that are needed to create perfectly dynamic and flexible layouts, so you really should only rely on HTML tables to present actual data. This being said, it happens that you don't have the choice, and that this just what I've learned recently in my work.

In the context of a client project, the framework I've had to use (Peoplesoft, an HRIS system) indeed generates HTML pages presenting a structure quite "2000"-ish, consisting in a complex nesting of HTML tables. Moreover, each time you add a component on the page (via the system itself, not when hardcoding it), a system TABLE is create to wrap the object. This can cause obviously a lot of problems, but in this case, my problem was quite specific.

A few pages of the system use a well-defined table layout : one column on the left (in red below), a top bar (in blue) and some pagelet (cells in green) below. Here is what a simplified version of that layout looks like:

 
So, what is exactly the matter here? Both the top bar and the cells below are meant to have a fixed height. Sadly, you cannot have a height of 800px for the left bar, while the top bar is only 100px high and the pagelets 200px (for instance). The browser will automatically stretch the cells on the right to match the height of the left bar. Therefore, what we get in this scenario is that the green cells are stretched to match a height of 700px (even though a height of 200px is explicitely applied). 

It might not be the best solution, but here's the trick I've used to cope with this: an extra row must be added below the green cells. Now, you cannot just add a row to the table, else it will be appended at the very bottom (below the red even), so that the problem would still be there. The thing is simply to increase the rowSpan of the left bar, so that the hack works. As a result, the blue and greens cells will have a fixed height (CSS or inline styling), while the newly added row (invisible) will stretch to fill the empty gap. Be sure to remove border (think of the border-collapse CSS property to ensure a perfect cell styling) so that the "filler" cell remains hidden to the user. 
 
This is the theory, but it is naturally more interesting to have a script doing that work for you (especially because you might not be able to access the TABLE properties for some reasons). Below is what I've come up with:


        jQuery(document).ready(function(){
            $("td[rowSpan]").each(function(){
                var R=parseInt($(this).attr("rowSpan"));
                $(this).attr("rowSpan",R+1);
                $(this).parent().nextAll("tr").eq(R-2).after("<tr class=table-filler><td><div>&nbsp;</div></td></tr>");
            });

        });


In a nutshell, the script searches for all TDs elements (cells) having a rowSpan attribute. When found, it retrieves that rowSpan value (R), update it (R+1) and finally add the filler after the appropriate number of rows. As a consequence, the script also works if the original left bar spans over 3, 4 or 5 rows (see below), can be applied several times and should not affect the rest of the HTML table beyond the left bar. 


Well, well...This seems all good, but a detail should not be neglected. To get this result, we assume we can set a fixed height on the blue and green cells. But what if only their inner HTML has a fixed height (ie: a DIV). If you don't explicitely define a height for the TDs, they will anyway all receive a default height that could turn your table into a mess. Here's a way the script can be improved to retrieve the size of the content of the cells, and apply it to the TD itself.

         jQuery(document).ready(function(){
            $("td[rowSpan]").each(function(){
                var R=parseInt($(this).attr("rowSpan"));
                var rSz=$(this).height();
                $(this).attr("rowSpan",R+1);
                $(this).parent().nextAll("tr").eq(R-2).after("<tr class=table-filler><td><div>&nbsp;</div></td></tr>");
                // siblings of the left column
                $(this).parent().find("td").not($(this)).each(function(){
                    // to get the best size, we wrap the content of the TD ($(this) in the new local scope) in an auto-sizing div
                    $(this).html("<div style='height:auto;'>"+$(this).html()+"</div>");
                    $(this).css("height",$(this).find("div").height()+"px");
                });
                // ...and all others TDs over which the left col spans
                $(this).parent().nextAll("tr").slice(0, R-1).find("td").each(function(){
                    $(this).html("<div style='height:auto;'>"+$(this).html()+"</div>");
                    $(this).css("height",$(this).find("div").height()+"px");
                });
            });
        });


Compare the two screen captures below. A div including an H3 title has been added in one of the green cells. No height has been defined for this cell.

As always, I'm only just tackling some bits and pieces of the problem and I'm just offering a basic workaround. The script can definitely be improved (or fixed, since I haven't tested it for hours just yet :-)). Still, in my case this does the trick and it helps me cope with the HTML DOM structure I'm forced to worked with. Again, when creating web layouts, try to stay away from tables if they are not absolutely required.

Commentaires

Posts les plus consultés de ce blog

Lightbox effect with jQuery (3) - ready-to-use library

I've been pretty busy with work over the past days, but I managed to finalize my lightbox javascript library still. Although I can't confirm it is 100% cross-browser, it seems to be running well under IE8/9, Firefox and Chrome. I reckon it should work as well on Safari, but you never know. The actual library and a sample HTML file can be found here: https://sourceforge.net/projects/delightbox/files/ How to use ? First, include jQuery and, needless to say, the library. Second, call the function init from dlb , once the DOM has been fully created and the page is ready. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="./delightbox-latest.js"></script>         <script>         jQuery(document).ready(function(){             dlb.init();         }); </script> Delightbox allows to create a lightbox e

A binary clock in Javascript

Just a short post to share a tiny project I recently worked on: a binary clock. The script is written in pure Javascript (no jQuery for a change), and the design of the buttons was rapidly made in Photoshop, so nothing fancy...but I really wanted to create this :-) http://manuthommes.be/toolbox/binaryclock/ As usual, the code is not obfuscated so you can have a look and feel free to copy/edit.