While we are in the final reviewing stage before submitting Store v 2.2 to the release process, we've been talking a lot with my co-lead Gilles about XHTML compliancy and the TABLE tags.
Table vs tableless layouts
As you know, XHTML compliancy says that we should avoid using tables for layout. Search the internet on that topic, and you'll immediately notice that the debate is heavily emotion-driven, with flames such as 'tables are bad’; 'tables vs css: a fight to the death’; 'why tables for layout is stupid’; 'tables hell, css heaven', etc.
So what's the problem with tables, and what's the problem with the alleged css solution?
First of all, let's admit that we have overused tables in the past. In those ancient times we would start any new HTML pages with a table block, even before we knew what content would get into. A common usage of the table was to use it as a spacer, or as a grid to draw an ellipse across the screen with a mosaic of small pictures. This made huge and unreadable pages with embedded tables, and a bunch of unused TRs, TDs and SPANs.
Cleaning these top level table containers and replace them with DIVs and SPANs is not very difficult, and this is exactly what Gilles has been massively doing with the Store code lately. At this stage, we might wisely conclude that CSS are best for layouts, and tables have to be used for the sole purpose of displaying spreadsheet-like tabular data.
Unfortunately, things are not that simple and problems appear when we reach the deeper parts of the code. I'll take the Store Catalog as an example.
The catalog displays a collection of products in rows and columns. Every product will typically contain a picture, a model number, a description and a price. This can be user defined by the corresponding template (e.g.: NewProduct.htm, FeaturedProduct.htm ...). Another interesting point is that the site admin can set any number of columns he wants, in order to display the products side by side.
So would you go table or table-less for the Store catalog?
Displaying the products is clearly a layout problem. As developers, we don't know which content will be published, and the same type of data will be repeated in all columns (I can hear the pundits of semantics purity arguing that tables should be reserved for tabular content where the type of data is different in every column - in other words when you can give every column a different title - which is not the case here).
So Gilles has done extensive investigations to find out how we could possibly expunge all the tables from the Store catalog. And he succeeded... but at a cost. Just decide by yourself:
Using DataList in Table mode or in Flow mode ?
It's not an easy job to suppress the tables which are generated by the DataList control. The DataList control offers two rendering modes: Table or Flow. Let's be good boys and try Flow mode, since Table mode is assumed bad practice. The Flow mode generates SPANs and injects inline CSS style attributes for some of them. These inline CSS attributes will override your other style sheets definitions. No matter what we can do, the control will inject its css, like: Style=”display: block;” for each row SPAN. So the ideal separation between the code and the rendering through CSS will be broken. No 'CSS heaven' here. In addition, this can produce junk XHTML if the store admin use tag blocks inside his templates.
Override the DataList rendering methods ?
A few investigations around the KeyTag and RenderBeginTag / RenderEndTag methods were proven useless in our case. Although it's good to know that you can (normally) override these methods, this is of little help in the case of the DataList control. Basically, the KeyTag overriding would allow you to change the default container tag of a control (say change a SPAN to a DIV or vice-versa); and the RenderBeginTag adds the flexibility to add attributes to this tag, typically as css class. It will not automatically produce a tableless layout. At last, although it is possible to override these two methods, the DataList control ignores them, probably because they are not implemented in the code.
More interesting was the RenderContents method. Since the DataList is more than anything else a rendering control, you might ask why use a DataList if we rewrite its rendering method - but we are in the quest for table-less purification, so let's see how far we can go.
protected override void RenderContents(HtmlTextWriter writer)
{
base.EnsureChildControls();
// Create the div container tag with a class attribute
writer.AddAttribute(HtmlTextWriterAttribute.Class, base.CssClass);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
// Get each DataListItem from the grid
foreach (DataListItem listItem in base.Controls)
{
// Create the div tag with a class attribute to simulate a row
writer.AddAttribute(HtmlTextWriterAttribute.Class, listItem.CssClass);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
// Get each column (product) from the DataListItem
foreach (Control product in listItem.Controls)
{
// Here we should insert a tag block, but which one?
product.RenderControl(writer);
}
writer.RenderEndTag();
}
writer.RenderEndTag();
}
This may be a challenging programming exercise, and if you want to go that way, Gilles will be interested at your results. Remember than the store admin can add anything in the corresponding template!
On our part, we prefer to use the good old DataList control in Table mode, that has served us so well in the past.
Templating inside SPANs
Rather than torturing the DataList, we might want to forget it completely using a Repeater control. But we would soon face another problem with templating. In our search for the ideal table-less catalog, we would probably consider something like a DIV container, containing a block tag for each line (which one: a div or a p tag?), and side-by-side floating-left SPANs to display our products. The problem is that SPANS are inline tags. This will not permit to include block tags in the product template. Cul-de-sac!
We could also use a div tag for each level (container, row, and column). Ugly! And how can we dynamically tell that the last column on the right should not be 'float-left’ without using inline Style attribute?
Paging
An ultimate problem is paging. Paging is a nice feature of the DataList control. If you want pure css, good luck with paging!
Conclusion
So the problem is that after successful experiments, we realized that the drawbacks of using a pure CSS approach would by far outweight the advantages. Even worse, we would reproduce with CSS, DIVs and SPANS the very same trouble that made tables undesired in the past (for no proven benefit). To name a few:
- heavy and unreadable code with a forest of embedded DIVs and SPANs
- uncertainties about the final rendering with various browsers
- destruction and lost of all the dynamic advantages of the DataList control, including paging
- no benefits for SEO, nor for accessibility
- absolutely no gain in performance or bandwidth usage
- no control over what the user will insert into the template anyway (hey you, what's that table in my pure css layout?)
The conclusion is that trying to suppress *all* tables from the Store module is an exercise in futility. After all, tables are not banned from XHTML. When we want to dynamically repeat content in columns and rows - especially when the number of columns must be parameterized, not only are tables permitted, but they are also the most elegant and efficient way to go.
Store v 2.2 will soon enter the release process with full XHTML compliancy, and far less TRs and TDs than before. But don't expect to find it without a table!