N.B.
Logo for "Nota Bene / non-blog" goes here.
Along with navigation links. NB main page
I’ve just read Bulletproof Web Design by Dan Cederholm, in which he shows example web pages with unique layouts and bad code, and recasts the same effect using semantically-meaningful XHTML markup with the effect applied using CSS 2.1.
After seeing the “RTE Colour Pallete [sic]” used by a forum, I’m inspired to do the same.
A note on spelling: No dictionary contains the word as originally spelt. Of the corrections supplied by onelook.com, “pallette” was listed before “palette”. The latter is more common. The former is a closer match, being one letter different instead of two. I like being contrary while still being correct—see “spectacle” as described by an art teacher long ago: it’s what makes the ordinary into art.
The item is part of a free rich text editor, credited at the top as
<!-- Web Wiz Rich Text Editor ver. is written and produced by Bruce Corkhill ©2002-2004 If you want your own Rich Text Editor then goto http://www.richtexteditor.org -->
It is 25,396 bytes in size. The W3C validator won’t even touch it without a little help, and then reports over 800 errors. HTML Tidy is more forgiving, generally reporting on what a browser might make of it and continuing. It finds 1045 HTML errors that it (and hopefully browsers) can automatically fix or put up with. That’s nearly an error every 25 bytes, and averaging nearly three per line of HTML!
A lot of the errors are simply multiple occurances of the same basic mistake. Having 252 nearly identical lines of HTML is an issue in itself that we’ll come back to. For now, look at one of the lines. The rest are all the same except for the number shown twice toward the beginning of the line, 000000 in this particular case.
<td id="#000000" bgcolor="000000" height="11" width="11"><img width="1" height="1"></td>
You might be interested to know that this line has 5 HTML errors on it, reported by HTML Tidy:
<td>
attribute id
has invalid value #000000
<td>
anchor #000000
already defined
<td>
attribute bgcolor
had invalid value 000000
and has been replaced
<img>
lacks alt
attribute
<img>
lacks src
attribute
The Wc3 validator doesn’t find the problem with bgcolor
, because the
SGML parser is
not checking constraints not present in the
DTD.
The DTD just says it is a string, as it doesn’t have an easy
way to enumerate the 16 color names and the syntax for RGB colors. The official specification
explains it in a comment, so it is unknown to the formal DTD interpreter.
The bgcolor
is easy to fix by adding the missing #
character. The ids are
fixed by removing the #
character, but ids can’t begin with a digit either so I prefixed
them all with “color_”.
Each IMG
tag complains about missing src
and missing alt
. The alt
tag is required for explaining what the picture is if the browser is not loading pictures. But missing the src
attribute‽ What is an image without a file containing the image? Not that a 1×1 image could be very interesting
anyway. The size of the table cell is already specified, and the color of that cell is the cell’s own bgcolor
, so what
good is the image? Since it doesn’t even exist, it can’t be doing much. So I deleted the entire IMG
tag, and
508 errors with it.
The Javascript code uses the element’s id
as the color to pick, so having the same color swatch in more than
one place gives us duplicate id
s. In fact, that is the only thing the id
is used for. So why can’t the code
use the actual bgcolor
of the item instead of the id
? Perhaps in the distant past it was not easy to determine the
bgcolor
property, with different browsers having different object models and all. If that were still the case, a fix would be to
add a nonce to the name and parse out the color number. But the whole concept is wrong if building to the standard, so I’ll take out the id
attribute altogether.
Finally, the page looks exactly the same (it doesn’t work though, since the Javascript needs to be updated to match), is down to 16K, and only has 8 errors left. The remaining errors are:
<script>
missing type
attribute
<body>
proprietary attributes leftmargin
, topmargin
, marginwidth
, marginheight
<table>
proprietary attribute bordercolor
(two occurrences)
<table>
proprietary attribute height
The missing script type is "text/javascript"
. Since I’m not sure what a marginwidth
is, I'll leave off
showing the portable/standard way to write that. The margins around the entire panel will be specified in CSS,
and might even be better off controlled by the parent document’s IFRAME
.
The bordercolor
attribute can be written as the border-color
style property. The
existing attribute style="none;"
doesn’t make sense as that is not valid CSS. Finally, the
table height
is simply removed, since the height is specified in every element anyway. The height
of the table ought to be the sum of the heights of its rows and borders, right? If it is something else, then
I don’t see the point of specifying the latter.
At last, it is valid HTML, is less than ⅔ the size, and looks exactly the same. You can do a diff to find all the changes in detail, including a few I didn’t elaborate on.
The HTML now looks like this:
<table border="0" cellpadding="0" cellspacing="0" bgcolor="#000000" width="100%" style="border-color:black"> <tr> <td bgcolor="#000000" height="11" width="11"></td> <td bgcolor="#000000" height="11" width="11"></td> <td bgcolor="#000000" height="11" width="11"></td> <td bgcolor="#000000" height="11" width="11"></td> <td bgcolor="#003300" height="11" width="11"></td> <td bgcolor="#006600" height="11" width="11"></td> <td bgcolor="#009900" height="11" width="11"></td> <td bgcolor="#00CC00" height="11" width="11"></td> <td bgcolor="#00FF00" height="11" width="11"></td> <td bgcolor="#330000" height="11" width="11"></td> <td bgcolor="#333300" height="11" width="11"></td> <td bgcolor="#336600" height="11" width="11"></td> <td bgcolor="#339900" height="11" width="11"></td> <td bgcolor="#33CC00" height="11" width="11"></td> <td bgcolor="#33FF00" height="11" width="11"></td> <td bgcolor="#660000" height="11" width="11"></td> <td bgcolor="#663300" height="11" width="11"></td> <td bgcolor="#666600" height="11" width="11"></td> <td bgcolor="#669900" height="11" width="11"></td> <td bgcolor="#66CC00" height="11" width="11"></td> <td bgcolor="#66FF00" height="11" width="11"></td> </tr> ...
Recall the earlier musing on the over-constrained table height. If every cell has the same height, doesn’t that mean that each row height is the height of the cells, too? So why bother putting the same height on every cell? Put the height on the row instead.
Certainly writing
<tr height="11"> <td bgcolor="#000000" width="11"></td> <td bgcolor="#000000" width="11"></td> <td bgcolor="#000000" width="11"></td> <td bgcolor="#000000" width="11"></td> ...
is an improvement, it is not necessary to specify the height of all 12 rows individually as the same 11 pixels. Instead, use CSS to say “In this table make each row 11 pixels high.” once.
That can’t go in a style
attribute of the TABLE
element, but has to go into the CSS rules for the page.
It is a good habit to get into anyway, to separate the style from the HTML. You see, it’s not just
cleaner, it is more powerful. You can only attach a style attribute to the element to which it directly applies. But
CSS rules can be general and automatically find everything it applies to. To make it work, I gave the TABLE
an id
.
Here is a real use of an id
!
table#pal TR { height:11px; } ... <table id="pal" border="0" cellpadding="0" cellspacing="0" bgcolor="#000000" width="100%" style="border-color:black">
As an aside, I originally tried to write table#pal > TR
, meaning only those TR
s that are direct children
of my table. That is a habit to prevent runaway formatting: in more general use, I don’t want the rule to apply to
the text (which may itself contain tables) within the item. But, TR
is not directly contained in the table, despite the
way the HTML source code looks. There is a <TBODY>
that is omitted from the source but still there in the DOM. So
the rule would have to be table#pal > * > TR
, but since this is a special table and not a general-purpose
style sheet, it’s not worth the trouble.
Looking at the html files in their own windows at some arbitrary size, they certainly look the same at a glance.
But put them in their precisely-sized IFRAME
s side by side and there are some slight differences visible.
Side-by-side, you can see that the upper section and the main pallette table are both shorter than
the original, resulting in extra room at the bottom when the frame is set to exactly 165 pixels high. Less
obvious in its frame but apparent if you looked in a separate window (or
can resize IFRAME
s with the mouse)
is that the original will resize vertically to fill the room, while the new one keeps the same row height even if
given more room.
The upper section is a bit of a mystery. Nothing changed in the first table or its contents. Yet it is one pixel shorter than it was originally! The problem lies in line 57:
...<div style="background-color: #000000; padding: 1; height: 15px; width: 40px">...
The Firefox error console tells me that there was an error parsing the value for the padding
property so
the declaration was dropped. That is true, 1
is not a legal value. It was intended to be 1px
.
Ignoring it made it zero instead. Yet it was not reported as an error in the original file, and took the padding as one pixel!
The difference is the presence of the <!DOCTYPE…
declaration. Without the DOCTYPE
, the browser uses a “quirks” mode, which
is intended to render things the way it worked in browsers that existed before good standards were written. If you put the DOCTYPE
into
the original file you get the same behavior with the padding, so the height of the upper portion matches that from the new code.
But if you try that you’ll see that nothing else works, and the main pallette table is all black! That’s because the bgcolor
is missing the #
character. The quirks mode guessed what was meant. Note that the behavior details can vary from browser to browser and
between versions as well. That’s the problem with banging on it until it looks right rather than following the standard and
removing all errors.
The size of the main pallette table is an interesting case, too. Although the original file states that every table cell has a height
of 11 pixels, inspecting the results shows that some of them turned out to be 12 and some 11. With the new code, every row was
sized to 11, as specified. This is caused by the third thing noticed, that the original resizes vertically. As presented in the IFRAME
, it has
already been resized, stretching a little from what was specified.
According to the HTML Specification, the
deprecated height
attribute supplies user agents with a recommended cell height.
Nothing else is said about row height, so what the
browser does is really whatever it wants. That may be why there were the meaningless IMG
tags in the cells;
perhaps some browsers of the vintage would reduce the recommended height if the cell was seen to be empty, and using a
wasn’t always working either—bang on it ’till it works, and hope for the best moving forward.
Using CSS to specify the height has a well-defined meaning, that is explained in some detail. Specifying the height as 11 pixels does
exactly that, without surprises. I like the self-sizing table, so let’s see what CSS can do in that respect.
The table should fill the room available, with no mention of any other height or width. Unfortunately, the CSS2.1 specification
does not do that. It doesn’t make you guess about it either. It quite clearly states,
Any other value [than
auto
] is treated as a minimum
height. CSS 2.1 does not define how extra space is distributed when the height
property causes the table to be taller than it
otherwise would be. Note. Future updates of CSS may specify this further.
Experimenting with using a height expressed in percentage did not prove fruitful. Even if that worked, the border has a problem, since that is sized in pixels and you can’t express the box outside dimensions, only the content dimensions. Rather than going to great lengths to convince at least one browser to resize vertically, I’ll stick with a fixed height of 11 pixels. It will be simple to change the CSS property, in one place, using Javascript based on the available size.
As long as we have a separated style for this specific table, I might as well move the rest of the junk into there. The attributes
border="0" cellpadding="0" cellspacing="0" bgcolor="#000000"
become CSS properties. The
cellpadding
corresponds to the padding on the individual cells, which is a property of each cell, not of the whole
table. The cellspacing
property corresponds to the border-spacing
property. We don’t have to
worry about subtle semantic differences because we just want them all to be zero. The table’s background color does not
matter because it is completely covered with elements and their borders.
table#pal { empty-cells: show; border: none; border-spacing: 0px; } table#pal TR { height: 11px; /* update this using JavaScript, based on available room. */ } table#pal TD { padding: 0px; width: 11px; }
Finally, just because it is so long, the size of the download can be further reduced by eliminating hundreds of optional closing tags. This brings the file down to under 9K, which is a fraction of the size of the original.
Because of an early change (fix, actually) in the HTML, the Javascript needs to be updated to match.
The Javascript uses the text of the id
attribute exactly as-is as the color string. This
is not right for several reasons: Id’s have to be unique, and they have to be syntactically correct. Instead,
I’ll simply use the background color from the cell.
Several places the original code uses this.id
to obtain the color string. An easy change that
offers good maintainability and future enhancement is to make a function that encapsulates the work. The existing code simply
needs to have this.id
replaced with get_swatch_color(this)
.
Further improvement would involve refactoring the whole design. That will be the subject of another page.