
A Confusion of Styles
by John E. Simpson
January 28, 2004
Q: How do I style a custom element's content?
I want two elements (QUESTION and ANSWER) to be declared in an external
DTD. The QUESTION element's data is to be displayed in red, and the ANSWER
element's data is to be displayed in green. For this, an external
stylesheet needs to be used. How do I include the DTD in my HTML
document?
Here's the relevant code:
queans.dtd:
<?xml-stylesheet type="text/css"
href="clr.css"?>
<!ELEMENT QUESTION (#PCDATA)>
<!ELEMENT ANSWER (#PCDATA)>
clr.css:
<STYLE>
QUESTION {font-family:arial;font-size:20pt;color:#ff0000}
ANSWER {font-family:arial;font-size:20pt;color:#00aa00}
</STYLE>
sample.htm:
<!DOCTYPE SYSTEM "queans.dtd">
<HTML>
<BODY>
<QUESTION>What is your favorite web
site?</QUESTION>
<ANSWER>My favorite web site is
www.xml.com.</ANSWER>
</BODY>
</HTML>
A: As you've no doubt discovered, if you open this sample.htm in your
favorite browser, you'll see the content of the QUESTION and
ANSWER elements all right but displayed in the browser's
default font (family, size, and color). You can fix this, but you'll have
to straighten out some major misunderstandings first.
To recap, you've got a DTD which defines a couple of elements; a
Cascading Style Sheet which contains a STYLE element; and an
HTML document which (via the DOCTYPE declaration) points to
the DTD and includes both elements defined there, plus a couple of others
(HTML and BODY). Among the numerous confusions
at work here:
- First, you don't need a DTD if all you want is to style an XML
element's content when viewed in a browser. (I'll show you how to do this
later.)
- Second, you really can't customize an HTML document by adding non-HTML
elements (such as
QUESTION and ANSWER), using a
DTD or by any other means. Browsers employ a variety of techniques to
determine what sort of file or document they're asked to present to the
user. (The simplest of these, also arguably the most common, is to look at
the file's extension.) If a browser thinks it's reading an HTML document,
it will display "known" HTML elements according to its default settings
(for font, color, and so on) for that element -- unless overridden by a
stylesheet. When it encounters an unknown element, however, all
bets are off: it just displays the content in the same font, etc., used
for plain old text (such as text contained in a p
element). Thus, your sample.htm file isn't a true HTML document; it's not
even an XHTML document, but a document
employing a hybrid, HTML-like XML vocabulary.
- Third, if for some other reason you want to use a DTD, it needs to
spell out everything which can be encountered in a document conforming to
the DTD. If a document referencing the DTD includes HTML and BODY
elements, then those elements must be declared in the DTD.
- Fourth, the association between a stylesheet and the content to be
displayed is never made in a DTD, or anywhere else for that matter, but in
the document where the content is found. Note that this is true for both
XML documents -- including XHTML ones -- and garden-variety HTML ones.
- If you're working with a non-(X)HTML vocabulary, use an
xml-stylesheet processing instruction just like the one
you've mistakenly placed in your DTD.
- If you're working with (X)HTML, place the reference to the external
stylesheet in a
LINK element -- again, in the document
where the content resides (sample.htm, in this case). (If you're using
XHTML-with-an-X, all element and attribute names must be lowercase. Thus
LINK becomes link, BODY becomes
body, and so on.)
- Finally, in a CSS stylesheet, there's never anything but the style
specifications: you especially don't include anything that looks like XML
or HTML (like the
STYLE element you've placed in
clr.css).
If contemplating all of this hasn't completely exhausted you, here are
some alternative solutions to your problem.
Simple customized display of an XML document
With this approach, as I said, you don't need a DTD at all. Just point
the XML document to the right stylesheet. Corrected versions of your
documents would then look as follows:
clr.css:
QUESTION
{font-family:arial;font-size:20pt;color:#ff0000}
ANSWER
{font-family:arial;font-size:20pt;color:#00aa00}
sample.xml:
<?xml-stylesheet type="text/css"
href="clr.css"?>
<HTML>
<BODY>
<QUESTION>What is your favorite web
site?</QUESTION>
<ANSWER>My favorite web site is
www.xml.com.</ANSWER>
</BODY>
</HTML>
Results when viewed in Mozilla:
(The Internet Explorer display is identical, except for the
browser "chrome" of course.)
Simple display of an XHTML document
This is a little more complex, but still simple. The stylesheet remains
the same as above. In the document itself, as I said, change all uppercase
element and attribute names to lowercase. Furthermore, while it's not
absolutely essential in the almost-anything-goes world of browsers, you
should formally associate your document with the XHTML vocabulary in two
ways:
(1) use a DOCTYPE declaration which points to the XHTML DTD of
your choice, and (2) use an xmlns attribute -- that is, a namespace
declaration -- to assert which of the document's elements are in the XHTML
namespace. (Typically, all elements in an "XHTML" document are in
the XHTML namespace, but this needn't strictly be the case.) And finally,
of course, you must add a link element to connect your
document to clr.css.
At this point, sample.htm will now resemble the following
(assuming you want to use the XHTML "transitional" vocabulary), with the
most significant changes in boldface:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css" href="clr.css"
/>
<body>
<QUESTION>What is your favorite web
site?</QUESTION>
<ANSWER>My favorite web site is
www.xml.com.</ANSWER>
</body>
</html>
In this case, browser behavior diverges: Mozilla's display of the
content matches that of the previous solution. Internet Explorer, however,
displays the question and answer in the browser's default font (family,
size, and color). The "problem," I think, is that IE still doesn't
recognize your QUESTION and ANSWER elements as XHTML. (Understandably, I
might add. This seems to be a more correct behavior than Mozilla's.)
The only way to fix this is to replace your QUESTION and ANSWER
elements with true XHTML elements, differentiating between them using
class attributes. Something like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css" href="clr.css"
/>
<body>
<span class="QUESTION">What is your
favorite web site?</span>
<span class="ANSWER">My favorite web
site is www.xml.com.</span>
</body>
</html>
The implication of this change is that you must also change
clr.css's selectors; here's a general solution:
.QUESTION
{font-family:arial;font-size:20pt;color:#ff0000}
.ANSWER
{font-family:arial;font-size:20pt;color:#00aa00}
(The dot preceding each selector associates that style with any element
which has a class attribute with the indicated value.)
Now both browsers again behave identically.
Also in XML Q&A
From English to Dutch?
Trickledown Namespaces?
From XML to SMIL
From One String to Many
Getting in Touch with XML Contacts
Getting fancier...
I don't mean "fancier" in terms of the display; I mean it in terms of
how to attain your objective. This solution builds on the
previous one; it assumes that you truly do need those non-(X)HTML elements
in your document, and you want to use the uppercase element names. It
simplifies some things, such as sample.htm itself (which
returns to something very like its original form). The complexity comes
from the addition of a second stylesheet -- this one in XSLT. Your
document would now look something like this:
<?xml-stylesheet type="text/xsl"
href="clr.xsl"?>
<HTML>
<BODY>
<QUESTION>What is your favorite web
site?</QUESTION>
<ANSWER>My favorite web site is
www.xml.com.</ANSWER>
</BODY>
</HTML>
The XSLT stylesheet in question would specify the transformation of
this simplified sample.htm into a result tree which more or
less matches the version of sample.htm in the previous
solution. Here's an XSLT stylesheet (just one of many approaches,
depending on how rigorous you need it to be) to accomplish this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<xsl:output method="xml"
version="1.0"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
/>
<xsl:template match="HTML">
<html>
<link rel="stylesheet" type="text/css" href="clr.css"
/>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="BODY">
<body><xsl:apply-templates /></body>
</xsl:template>
<xsl:template match="QUESTION | ANSWER">
<span class="{name()}"><xsl:apply-templates
/></span>
</xsl:template>
</xsl:stylesheet>
(I'll leave readers to extract from this example those bits which most
interest them.)
And how, you might wonder, do the browsers treat
sample.htm now? Sadly, they ignore -- at least under Windows
-- the result tree from this transformation; indeed they don't attend to
the xml-stylesheet declaration at all. I suspect this is because of that
operating system's stubborn reliance on filename extensions to determine
how to treat the document: .htm (or .html) means
the document is an (X)HTML document and damn the consequences.
Accordingly, sample.htm displays exactly as it did way back
at the beginning of this answer, in default fonts and colors.
(Rather than shake your head in misery at this point, you might try
changing the extension to .xml. Test it in both
browsers. You'll find something else to shake your head over.)
For what it's worth, if you run this most recent version of
sample.htm through a standalone XSLT engine like Saxon,
you'll see the result tree you need:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css"
href="clr.css"/>
<body>
<span class="QUESTION">What is your favorite web
site?</span>
<span class="ANSWER">My favorite web site is
www.xml.com.</span>
</body>
</html>
If you save this result tree to a separate file, the browsers handle it
just fine (and identically).
You may find this last alternative -- even though it's in some ways the
most correct, requiring the least modification to your original source
document -- a bit out of reach for now. Don't despair. Just keep asking
questions and doing research.