Despite all the ranting about tables vs. CSS, one place where they are unequivocally suitable is when displaying tabular data (though some people will mysteriously argue even about that). However, despite the fact that native OS controls for displaying such data offer all sorts of features, (X)HTML tables only provide the most basic functionality. Some enhancements have been made, but they are mostly for the sake of semantics or accessibility (
<tbody>, etc.) The W3C has only given us weakly worded proposals such as "User agents may exploit the head/body/foot division to support scrolling of body sections independently of the head and foot sections." (emphasis mine).
For an ongoing project I need a little more, and so I have come up with this more capable approach. Though it is still feature-poor compared to a native control, it is still a step forward (if we can have a wrapper for NSSearchField perhaps one for the data browser/NSTableView is warranted too?).
Firstly, column headers now stay put while the rest of the table is scrolled. This is primarily accomplished via the
position: fixed CSS attribute. However, since IE lacks support for it, an alternative approach (suggested by this page) using dynamic properties had to be used. This basically sets the
top attribute to a script snippet that is dynamically (re)evaluated to the document's vertical scroll offset. This does have the drawback that the header row had to be put in a separate table from the
<tbody> portion, and in order to get the cells of these two sections to line up, the
table-layout: fixed attribute had to be used.
However, there is a good reason to use fixed table layout anyway, since it allows us to implement the second feature, resizable columns. Making the dividers draggable was done by overlaying on top of their borders some
move handlers in a similar matter to the magnifier. However, instead of moving a
<div>, we compute the horizontal percentage of the mouse's position and use that to change the table's column widths*. Another trick was to have the draggable column divider
<div> change its width when a drag begins. Normally it is only ten pixels wide, centered on the column border. However, once a drag begins it expands to fill up the entire width of the page. The illusion of movement is accomplished by shifting the background position. This widening is to prevent the mouse cursor from escaping from outside its boundaries if it's moving too quickly. Attaching the
onmousemove handler to the document's body didn't work with Mozilla/Firefox, since the handler wasn't invoked when it was on top of the bare body (as opposed to another node). Finally, text selections were disabled since the effect was distracting when dragging a column divider. To accomplish this required the use of the
-moz-user-select CSS property in Mozilla-based browsers (based in turn on the
user-select property in a CSS3 draft). Safari and IE do not implement it, but they do support the
onselectstart handler, and setting it to a function that returns
false prevents selections in those browsers.
Incidentally, a bit of Googling turned up the fact that apparently the resizable columns feature (as implemented for IE only) is worth $24.95, so clearly this entry is quite a bargain.
* For performance reasons only the header widths are updated dynamically, with the rest of the table being refreshed only at the end of the drag. To update everything dynamically, it is only necessary to set the argument to