Learn More





DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

Store, XHTML and the Tables dilemna

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)
            // Create the div container tag with a class attribute
            writer.AddAttribute(HtmlTextWriterAttribute.Class, base.CssClass);
           // 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);
               // Get each column (product) from the DataListItem
                foreach (Control product in listItem.Controls)
                   // Here we should insert a tag block, but which one?

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?


An ultimate problem is paging. Paging is a nice feature of the DataList control. If you want pure css, good luck with paging!



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!



Comment Form

Only registered users may post comments.


2sic Daniel Mettler (124)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (21)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (268)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?