Use Google as Your Gateway to the Mobile Internet

If you'd like to offer your content to mobile users in a slightly more mobile-friendly format, you don't necessarily have to redesign all your web pages. Google has released a well-hidden Google Wireless Transcoder that transforms any HTML page into WAP (WML or XHTML) format. My InformIT article Use Google as Your Gateway to the Mobile Internet describes how you can benefit from this service and how you can use it to make your content more accessible to mobile users.

Safari does not URL-encode arguments of the document() function

David Peterson has found an interesting Safari bug: it looks like most other XSLT-capable browsers automatically URL-encode the invalid strings passed to the document() function, whereas Safari uses the input argument as-is (and fails if it contains unexpected characters).

Still, don't forget that URL encoding is the programmer's job and that the browsers only fix your errors when they do the encoding behind the scenes. However, properly encoding a URL string with the limited set of string-handling functions of XSLT 1.0 is close to Mission Impossible ... although of course it's been proven that it can be done even with a Turing machine ;)

And, last but not least, if you need a more detailed description of URL encoding - here it is.

Output UTF-8 text from ASP script

I had to write an application that outputs CSV text encoded as UTF-8 from an ASP script. This is what I've ended with:
Sub OutputCSV(finalText)
  Response.Clear
  Response.Charset = "utf-8"
  Response.ContentType = "text/plain"
  Response.Codepage = 65001
  Response.Write finalText
  Response.End
End Sub
In my application, the complete CSV data was stored in the finalText variable. If that's not the case, replace the Response.Write statement with the CSV-generating logic.

Using document function with URI in XSLT

I've encountered an interesting question on an XML forum recently: “Why can't I use the document() function to download XML content from another web site?”

The reason is very simple: the document function in XSLT can be a very problematic cross-site-scripting (XSS) tool if it's allowed to download a document from any URI. Therefore it should be limited to the domain of the XSLT stylesheet calling it, similar to the restrictions imposed on the XMLHttpRequest object. Internet Explorer 7 and Firefox 2.0.0.11 enforce this restriction and Opera decided not to support the document() function even when they've finally added XSLT support (and here is an ecstatic post claiming they do it now).

Enhance Your AJAX Applications with Visual Cues

In the Enhance Your AJAX Applications with Visual Cues article published by InformIT, I'm describing various techniques you can use to help your users get the most out of your AJAX application.

Select XML elements in XSLT template

I've stumbled across a fantastic thread discussing how you can use various XSLT mechanisms (for example, xsl:apply-template with select option versus xsl:template with XPath match expression). Highly recommended reading :)

Text nodes in XSLT

It's amazing how many people have issues with whitespace handling in XSLT. I admit it's counter intuitive unless you start thinking in XML terms (remember: XSLT stylesheet is first parsed by an XML parser). The rule I'll explain today is very simple: whitespace-only text nodes from XSLT stylesheet are not copied to the output.

In the example mentioned by Alexandar …
<input type="submit">
  <xsl:attribute name="value">
    Login
  </xsl:attribute>
</input>
… a single text node within the xsl:attribute element (as parsed by XML parser) includes the Login text as well as a few whitespace characters. To get rid of the extra whitespace characters (it looks like the target application hates having leading whitespaces in submit button names), you could write the whole value in-line (now there is no extra whitespace in the attribute value):
<xsl:attribute name="value">Login</xsl:attribute>
Alternatively, you could split the contents of the xsl:attribute element into whitespace-only text nodes (which are not copied to the output) and a non-whitespace text node (containing the Login text). The easiest way to do it is to insert an xsl:text element within the xsl:attribute element:
<xsl:attribute name="value">
  <xsl:text>Login</xsl:text>
</xsl:attribute>

This post is part of You've asked for it series of articles.

Search Engine Optimization in XML+XSLT designs

It's amazing that the question “how do I perform SEO for a web site that does XSLT transformation on the browser” still pops up, as the short answer is very obviously “You can't.”The long answer is, as always, a bit more complex:
  • Google can process XML data, but only stores it into supplementary index (lower rankings)
  • If the server's XML output does not contain enough context (for example, product description in HTML-ish format), the search engines cannot make any sense out of it, so it would not be indexed appropriately.
  • Search engines will not follow explicit (let alone implicit) links in XML documents, so you need a sitemap (classic HTML page, Atom/RSS feed or Google sitemap) to help search engines find the content pages
Until Google (and other search engines) implement XSLT transformations, the only sensible approach is to detect the client capabilities on the server and perform the XSLT transformation on the server if the client cannot do it (the whole architecture is described in my InformIT article Optimized Presentation of XML Content that was published almost exactly a year ago).

Dynamic output elements in XSLT

Sometimes the output element that has to be emitted by an XSLT transformation is not known in advance. For example, using the book structure from one of the previous posts
<book title="Sample book">
  <chapter title="First chapter">
    <section title="Section in first chapter">
      <section title="Chunk of text within a section">
        <para>My paragraph</para>
      </section>
    </section>
  </chapter>
</book>
… we'd like to generate HTML H1 heading for the chapter and H2Hx headings for the sections. It's obvious that the heading element's name can be computed easily as 'H'+number-of-ancestors. The corresponding XSLT template is of course a bit more complex and uses the xsl:element tag to generate the output element.
<xsl:template match="chapter|section">
  <xsl:element name="{concat('h',count(ancestor::*))}">
    <xsl:value-of select="@title" />
  </xsl:element>
  <xsl:apply-templates />
</xsl:template>

The xsl:element expects a QName (a string specifying the element's name) as its name attribute. To insert dynamically computed element's name, you have to use curly braces around the expression.

Add a truly modal dialog box to your web application

If you do a search on "JavaScript Modal Dialog", you'll get a lot of hits and a variety of solutions … but most of them tend to forget that the browser users can use the keyboard keys as well as the mouse to navigate around the web page. The solution I'm describing in my new InformIT article Building Modal Dialogs into Your Web Application addresses both input devices, giving you a truly modal dialog box that captures all key- and mouse clicks.

XSLT global replace

I found a code to do a global replace (similar to VBScript Replace function) in XSLT. It's a great example of important XSLT 1.0 concepts: setting variable value with other XSLT tags (function call emulation) and changing a loop into a recursion (as loops are not available in XSLT 1.0). Here is the slightly cleaned-up code. First we define the function (named template) and its parameters:
<xsl:template name="globalReplace">
  <xsl:param name="value"/>
  <xsl:param name="from"/>
  <xsl:param name="to"/>
The xsl:choose is the if-then-else/select replacement. We test if the source substring is present in the value …
  <xsl:choose>
    <xsl:when test="contains($value,$from)">
… if it is, we do tail recursion first, replacing all the remaining values and storing the result in $rest …
      <xsl:param name="rest">
        <xsl:call-template name="globalReplace">
          <xsl:with-param name="outputString" select="substring-after($value,$from)"/>
          <xsl:with-param name="from" select="$from"/>
          <xsl:with-param name="to" select="$to"/>
        </xsl:call-template>
      </xsl:param>
… and then concatenate the substring before the first from value with the to value and the results of the tail recursion.
      <xsl:value-of select="concat(substring-before($value,$from),$to,$rest)" />
    </xsl:when>
If the source string does not contain the substring to be replaced, we just return it (this also ends the tail recursion) …
    <xsl:otherwise>
      <xsl:value-of select="$outputString"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

A few must-read JavaScript documents

If you're serious about JavaScript programming, read these first:

XSL: Copy input elements into output stream

The usual answer to the question “how do I copy input XML elements into output XSLT stream?” is “use xsl:copy or xsl:copy-of for deep copy.” But, as always, the devil is in the details. For example, if you're faced with a product catalog structure similar to this one …
<catalog>
  <product id="123">
    <name>Sample product</name>
    <text>A <strong>marvellous</strong>
      <span class='red'>red</span> book</text>
  </product>
</catalog>
… and use a simplistic approach to the copy-of
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:if test="text">
    <xsl:copy-of select="text" />
  </xsl:if>
</xsl:template>
… you'd end up with an extra non-HTML-compliant text node in the output stream …
<h3>Sample product</h3>
<text>A <strong>marvellous</strong>
 <span class="red">red</span> book </text>
To get just the contents you'd like to get, you should still use the xsl:copy-of instruction, but select just the child element (child::*) and the text nodes (text()) of the text element. To add icing on the cake, we'll enclose the contents of the text element in a P tag:
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:for-each select="text">
    <p><xsl:copy-of select="text() | child::*" /></p>
  </xsl:for-each>
</xsl:template>

Displaying element's ancestors

If you need to process the whole path from the root element to the current element, the ancestor:: axis provides a convenient means of doing that, more so as it lists elements from the root element toward the current one (the opposite direction from what you'd get with the parent:: axis). For example, assume you have an online book with the following structure:
<book title="Sample book">
  <chapter title="First chapter">
    <section title="Section in first chapter">
      <section title="Chunk of text within a section">
        <para>My paragraph</para>
      </section>
    </section>
  </chapter>
</book>

The section elements can be nested

You could use the following template to display the breadcrumbs in front of each heading:

<xsl:template match="chapter|section">
  <p class='crumbs'>

Walk through all the ancestors, starting from the root element

    <xsl:for-each select="ancestor::*">

If the current element has an ancestor, we've obviously printed something already, so insert a breadcrumb separator …

      <xsl:if test="ancestor::*"><xsl:text>, </xsl:text></xsl:if>

… and display the title of the ancestor element.

      <xsl:value-of select="@title" />
    </xsl:for-each>
  </p>

At the end, display the current heading and recursively process child elements.

  <h2><xsl:value-of select="@title" /></h2>
  <xsl:apply-templates />
</xsl:template>

Max function in ASP

The VBScript (at least its ASP dialect) is not very strong in math functions. For example, the simple Max function is missing. It's not hard to write, though:
Function Max(A,B)
  Max = A : If B > A Then Max = B
End Function
Things get a bit more complex if you want to take in account the NULL values (assuming non-NULL is always greater than NULL):
Function Max(A,B)
  Max = A : If IsNull(B) Then Exit Function
  If B > A Or IsNull(A) Then Max = B
End Function
However, I prefer the multi-value function that can take as many arguments as you like (unfortunately in an array, as VBScript functions can't take variable number of arguments):
Function MaxArray(A)
  Dim I
  MaxArray = A(LBound(A))
  For I = LBound(A) + 1 To UBound(A)
    If A(I) > MaxArray Or IsNull(MaxArray) Then MaxArray = A(I)
  Next
End Function

The code uses a VBScript quirk: function name without parenthesis when used within the function refers to the current function's value, not a recursive call

To call the MaxArray function, you don't have to construct an array beforehand, you can also use the array VBScript function, for example:

mxv = MaxArray(array(A,B,C,D))

Keyboard shortcuts in web user interface

Keyboard shortcuts can make an application significantly quicker to use; however, if you want to implement then in your web applications, you'll have to wade through murky waters of non-standard keyboard events (the only major browser event type not standardized in currently supported DOM standards). I've documented most of the pitfalls (and the workarounds) in my InformIT article Adding Keyboard Shortcuts to Your Web User Interface.

Firefox vulnerabilities when using data: or jar: protocols

Just stumbled across two articles describing vulnerabilities in the ways Firefox handles the data: and jar: protocols. If you're at least a bit interested in securing web applications (or your network), read them (and try to understand what's going on). It looks like the data: protocol vulnerability is a bug, but the jar: thing is a clear FAD (Functions-As-Designed).

XSL function to find parent node name

If you want to find the node name of the current element's parent, use local-name(parent::*):
  • The local-name function returns the name of its argument (without the namespace) or the name of the current element if it's called without an argument;
  • The parent:: axis selects the current element's parent;
  • The parent::* matches any node that is the parent of the current element.
For example, given the following input data …
<book>
  <chapter>
    <section>
      <title>My section</title>
      <para>My paragraph</para>
    </section>
  </chapter>
</book>
… this XSL template will output “My parent is: section
<xsl:template match="para">
  My parent is: <xsl:value-of select="local-name(parent::*)" />
</xsl:template>

This post is part of You've asked for it series of articles.

Fading background made simple

I wanted to implement fading background for HTML elements that are changed in the background based on XMLHttpRequest calls made in my AJAX applications, the idea being that the user's attention would shift toward the changed element without the visual cue being too intrusive. As I've had excellent experience with the X library, I started my search there … and found the xAnimation.rgb function that does exactly what I've needed. The following short function changes the element's background from page default (white) to dark yellow and back in a second:
function setFading (e) {
  e.style.backgroundColor = "#FFFFFF";
  if (!e.xa) e.xa = new xAnimation();
  e.xa.rgb(e,"background-color","#EDC226",1000,1,1);
}

Emulating activeElement property with DOM browsers

Sometimes you need to know which page element has the focus. Internet Explorer solves the problem nicely with the document.activeElement property, which is unfortunately a proprietary extension and thus not available in other DOM-compliant browsers.

All the solutions I've found with Google were kludges along the lines of “scan all the elements and test each one to see whether it has focus”. It's much better to use the capture phase of the DOM event model and track the focus and blur events with the document object:
function _dom_trackActiveElement(evt) {
  if (evt && evt.target) {
    document.activeElement =
        evt.target == document ? null : evt.target;
  }
}
 
function _dom_trackActiveElementLost(evt) {
  document.activeElement = null; }
 
if (document.addEventListener) {
  document.addEventListener("focus",_dom_trackActiveElement,true);
  document.addEventListener("blur",_dom_trackActiveElementLost,true);
}

This implementation is slightly over-pessimistic; if the browser window loses focus, the activeElement is set to null (as the input control loses focus as well). If your application needs the activeElement value even when the browser window doesn't have the focus, you could remove the blur event listener.

You cannot compare strings with XSLT

While developing an XSLT-based solution today, I've stumbled across an almost unbelievable fact: you can only test whether two strings are equal or unequal in XSL stylesheets (for example, in xsl:if or xsl:when conditions), not whether one is larger or smaller than the other (in the usual alphabet-based sense). The greater-than and less-than operators only work on numbers.

Before anyone rushes forward and tells me that XPATH 2.0 has compare function that does exactly what I need, let me remind you that XPATH 2.0 is only implemented in a handful of standalone XSLT translators and is (at least as of October 2007) completely useless when you need browser-based transformation.

And just in case someone starts explaining about the collating sequences, let me conclude this post by saying that you can sort on strings with the xsl:sort instruction in XPATH 1.0, but cannot compare them. I'm really wondering what the standard designers were thinking …

XML Handling in Microsoft SQL Server 2005

InformIT.com has just published my article describing how you can use XML data type in SQL Server 2005 to optimize SQL queries and updates.

XSLT-generated documents are never rendered in quirks mode in Firefox

I'm a heavy user of browser-side XSLT transformations (you can learn more about the techniques I'm using in the article I wrote for InformIT.com). While I'm usually generating HTML 4.0 (or event XHTML-) compliant transformation results, every now and then I would like to put the browser in quirks mode, for example, when I would like the text of my page to span the whole browser window (which is easily done with a TABLE with height set to 100% in quirks mode).

While this trick works nicely in Internet Explorer, Firefox produces standards mode layout (with TABLE contents condensed at the top of the screen) regardless of the HTML output the XSLT transformation generates. Probably this is due to the fact that the XSLT transformation in Firefox does not produce interim HTML string, but transforms DOM tree into another DOM tree.

Create your own animated …loading… indicator

I've needed a copyright-free animated GIF that I could use as a rotating wheel in my “Loading, please wait” overlay DIV. Asking Google about loading animation returned www.ajaxload.info as the first hit. It turned out to be an excellent web site that builds you an animated GIF with foreground and background colors you specify out of more than a dozen templates. Highly recommended ;)

Use xsl:key to link nodes in input XML document

In a previous post, I've described how you can use local variables and XPath expressions to link elements in the input XML document. An even better (and probably faster) alternative is to use the XSL keys. For example, you might want to link the target element with the source element in an XML document based on the ref attribute of the source and the id/@num attribute of the target:
<data>
  <source id="abc" ref="123" />
 
  <target>
     <text>Message</text>
     <id num="123" />
  </target>
</data>
You can define a key that will select the target element or another one that will select the text child of the target element (in which case you have to be a bit more creative in the use part of xsl:key definition):
<xsl:key name="target" match="target" use="id/@num" />
<xsl:key name="ttext" match="target/text" use="../id/@num" />
Assuming you want to display the text child of the target node, you could use either key in your transformation. If you use the target key, it returns the target node, so you have to continue with the XPath expression to get its text child; using the ttext key gives you the text node immediately:
<xsl:template match="source">
  <xsl:variable name="target" select="@target" />
  Source <xsl:value-of select="@id" />
    target <xsl:value-of select="key('target',@target)/text" />
    ttext <xsl:value-of select="key('ttext',@target)" />
</xsl:template>

Floating misteries

I guess all would be well if we would just take enough time and read through the CSS standards (at least through the sections documenting the box model and layouts). As that will probably never happen, floats remain a black magic to a lot of people (myself included sometimes). The Make Your Site BulletProof With Floats article published by Peachpit.com could make things a bit clearer … or at least you'll find a few good working examples.

Mouse event bubbling might produce unexpected results

The unexpected consequences of the mouse event bubbling are best illustrated with an example. Consider the following HTML markup (and note that in real life you should not declare event handlers in HTML markup):
<div onmouseover="d_a()" onmouseout="d_b()">
  <p onmouseover="p_a()" onmouseout="p_b()">Line 1</p>
  <p>Line 2</p>
</div>
When the mouse is moved from outside the DIV to the first line, d_a and p_a are called as expected. When the mouse moves from Line 1 to Line 2, p_b (expected), as well as p_a (highly unexpected) are called even though the mouse has not left the DIV. p_a is called due to event bubbling; the mouseover event bubbles from its target through all the target's ancestors toward the document object.

Probably the best workaround is to compare the event target (browser-dependent, use xEvent to get browser-independent code) with the current element.

On-demand load of JavaScript libraries

With a creative approach to DOM model, you can load JavaScript libraries on demand from your web pages. What you need to do is to create a SCRIPT element, set its SRC attribute and insert it into the document. You could use a function similar to this one ...
function LoadScript(url)
{
  var lib = document.createElement(’script’);
  lib.type=’text/javascript’;
  lib.src= url;
  document.getElementsByTagName(’head’)[0].appendChild(s);
}

... or you could use a safer xLoadScript function from the X library.

The X library also provides a xSmartLoadScript function to stop you from loading the same library multiple times.

In a recent post on PeachPit.com, Kris Hadlock gives you a few ideas where this functionality might come handy.

Link multiple elements from source XML data

An interesting question was asked in the Sun's Java forums: “How do I match attributes on different input nodes?” For example, I would like to link the target attribute of the source element in the following XML data with the target node (based on the num attribute of its id child).
<data>
  <source id="abc" target="123" />
 
  <target>
     <text>Message</text>
     <id num="123" />
  </target>
</data>
While it's easy to select the correct target node, it's harder to get the source attribute into the XPath expression; the only way to do it is to store the source attribute in a local variable and then use the variable value (which is context-independent) in the XPath expression:
<xsl:template match="source">
  <xsl:variable name="target" select="@target" />
  Source <xsl:value-of select="@id" />
    is associated with
  <xsl:value-of select="//target[id/@num = $target]/text" />
</xsl:template>
The final XPath expression works as follows:
  • It selects a target node anywhere in the source XML tree (the // path) such that the num attribute of its id child is equal to the local variable target
  • When the target node is selected, the value of the XPath expression is the value of its text child, which is then rendered into a string (collapsing all its descendant text nodes into the final result).

This post is part of You've asked for it series of articles.

How to handle XSLT namespaces

The Teaching XSLT post by Jeni Tennison is focusing mainly on the challenges of teaching XSLT (and I've really enjoyed that angle as I was also a CCSI for a long time), it also contains good recommendations on how to handle the namespaces in XSLT.

Some XSLT best practices

The XPath and XSLT without the pain presentation by Bertrand Delacretaz has some interesting recommendations for XPath and XSLT best practices. Highly recommended.

Match a keyword in space-separated keyword list (attribute value)

A post in Push Button paradise explains how you can use XSLT functions to test whether a keyword is present in space-separated list of keywords (for example, whether an HTML tag has a specific CSS class).
Notes:
  • The original problem explanation is somewhat misleading; the @class = 'vcard' test will correctly match if the class attribute is set to vcard, but would not match if vcard is just one of several classes applied to an HTML tag.
  • The Microformats wikipedia has code samples solving the same issue in multiple programming languages.
  • The best function I've found so far in JavaScript world is the xGetElementsByClassName from the X library.

Storing XML Data in a Relational Database

The Storing XML Data in a Relational Database article just published by InformIT.com describes the various methods you can use to store XML data in an SQL database. I've also tried to explain when it would be appropriate to store XML data in a database and when you'd be better off using the traditional relational database model. The rest of the article details the procedures you can use to insert, query, retrieve and modify the stored XML data.

Interesting: greater-than character does not need to be escaped in XML

I always thought that less-than, ampersand and greater-than character have to be escaped in XML. As Micah Dubinko points out in his blog post, that's not strictly true; greater-than character usually does not have to be escaped (here is the relevant part of the XML standard).

DOM events don't have the “on” prefix

One of the very common mistakes when switching from traditional object-property-based event registration (window.onload = handler) to DOM event registration (window.addEventListener('load',handler)) is the reuse of object property as the event name. The “on” property prefix should not be part of the event name (for example, use load instead of onload and mouseover instead of onmouseover).

To make matters worse, the attachEvent method (Microsoft equivalent of the addEventListener DOM method) expects the event names with the “on” prefix.

Serve SQL data in XML format

InformIT has just published my next article that explains various options you have when your data resides in an SQL database and the client side of your AJAX application expects the data in XML format. The article covers a number of different approaches, including:

Firefox hides the ?xml processing instruction

When I was testing the DOM handling of the XML processing instructions in various browsers, I've found an interesting inconsistency: Internet Explorer includes the <?xml ?> processing instruction in the DOM tree, while Firefox hides it.

For example, when the following XML document ...
<?xml version="1.0" encoding="windows-1250"?>

<?xml-stylesheet href="countryPage.xsl" type="text/xsl"?>

<Test />
... is transformed into a DOM tree and processed with this JavaScript function:
function testPI(dom) {

var topNodes = dom.childNodes;

for (var i = 0; i < topNodes.length; i++) {

var node = topNodes[i];

wr("nodeType="+node.nodeType);

if (node.nodeType == 7) {

wr("PI="+node.target+" ... "+node.data);

}

}
... Internet Explorer generates the following text:
nodeType=7
PI=xml ... version="1.0" encoding="windows-1250"
nodeType=7
PI=xml-stylesheet ... href="countryPage.xsl" type="text/xsl"
nodeType=1
... while Firefox skips the <?xml ?> processing instruction:
nodeType=7
PI=xml-stylesheet ... href="countryPage.xsl" type="text/xsl"
nodeType=1


IP address to physical location mapping

On every web site where I can configure the web server parameters (sadly not available in all hosting environments), I catch the ASP errors (500;100 error page), acknowledge them to the web user and report them in an e-mail to the webmaster (if you can't debug every possible code path, it makes sense to fix the bugs once the first visitor hits them).

Sometimes I would also like to figure out where the visitor causing the ASP error is coming from (more so if the errors are due to lack of resources, indicating unexpectedly high load). The usual tools (nslookup) fail if the ISPs don't enter their IP address space in reverse DNS lookup zones. In the past I've tried to use various Internet registries to figure out who the address is allocated to … and finally I've stumbled on the IP2Location web site, which does exactly what I was looking for: I enter the IP address and it tells me where the visitor is coming from (including a nice overview map for those who have no clue about geography).

Publish code samples in Blogger

Publishing the code samples in Blogger has always been a pain in the ***: the quoting of angle brackets doesn't work very well unless you do it manually, the leading whitespaces get lost (more so if you use spell checker), the line breaks are sometimes lost as well ... Finally I gave up and wrote a small PERL script that takes your code on standard input (usually supplied as a file name in the command line) and leaves the Blogger-ready markup on the clipboard, ready to be inserted in your post:
use strict;
use Win32::Clipboard;

our $lcnt = 0;
our $result = "" ;
our $CLIP = Win32::Clipboard();

while (<>) {
  if (/\n/) { chop; }
  $_ =~ s/\&/&amp;/gi;
  $_ =~ s/\</&lt;/gi;
  $_ =~ s/\>/&gt;/gi;
  while (/^\s/) { $_ =~ s/^(\s*)\s/$1&nbsp;/gi; }
  $result .= ($lcnt == 0 ? "<pre class='code'>" : "\n") ;
  $result .= $_;
  $lcnt ++;
}
$result .= "</pre>";
$CLIP -> Empty();
$CLIP -> Set($result);
print "Result is on the clipboard";

If you're not fluent in PERL, don't even try to understand what it does ... it works :)

Extract the default stylesheet from the DOM document

If you use XSLT transformations to change the XML data into HTML markup and use the xml-stylesheet processing instruction to specify the default XSLT stylesheet to use for the transform, you might need to fetch the default stylesheet value in JavaScript when developing AJAX applications.

You can find a good case study on using XML and XSLT in my Inform-IT article Optimized Presentation of XML Content.


The following JavaScript function extracts the default XSLT stylesheet from the XML document (passed to the function as DOM Document object). It relies on the fact that the DOM Document object extends the Node object and thus has the childNodes property that contains the root element as well as all processing instructions.

function getXMLStylesheet(dom) {

var topNodes = dom.childNodes;

for (var i = 0; i < topNodes.length; i++) {

var node = topNodes[i];

if (node.nodeType == 7) {

if (node.target == "xml-stylesheet") {

var match = /href="(.*?)"/gi.exec(node.data);

if (match.index >= 0) return match[1];

}

}

}

}


A typical usage of this function is illustrated below:
<script src="/sarissa.js"></script>

...

<script>

var xrq = new XMLHttpRequest();

xrq.open("GET","getPI.xml",false);

xrq.send(null);

if (xrq.status == 200) {

var dom = xrq.responseXML;

var xsl = getXMLStylesheet(dom);

alert("stylesheet="+xsl);

}


Constants of the DOM Node object are not implemented in Internet Explorer

The DOM Node object defines a set of constants that can be used to identify the node type. These constants are available in Firefox (and, I would assume, all other Gecko-based browsers), but not in the Internet Explorer. The expression ...

if (node.nodeType == node.TEXT_NODE) { ... }

... which would be largely self-documenting thus has to be written as ...

if (node.nodeType == 3) { ... }

... unless you define the same constants in your JavaScript.

Use JavaScript Progressive Enhancement to simplify HTML table formatting

In the Enrich Your HTML Tables with JavaScript Progressive Enhancement article I wrote for InformIT.com, I'm describing how you can use JavaScript progressive enhancement techniques to add last touches to your HTML table format after the web page has been loaded into the web browser ... and, no, I'm not talking about page layout with tables :), I'm talking about formatting data that deserve tabular format.

Should I encode data as XML attributes or child nodes

This question pops up quite commonly: should I encode my AJAX data as XML attributes or as child tags with actual value in the text of the tag? The answer, as always, depends on your data structure:
  • If your data is simple and non-hierarchical (for example, results of SQL query), using XML attributes will reduce the response size (due to shorter markup)
  • If you have repeating values in your data set, you cannot use attributes, as XML allows only a single attribute with a certain name per tag. In this case, you have to use child tags to encode repeating values. However, you can still transfer the actual value as an attribute of the child tag, not as text in the tag.
  • If you're transfering large character fields, using child tags will increase the readability of the XML response when you're inspecting it with a browser (you know, sometimes we have to do a bit of debugging :)
  • You might hit platform-dependent limitations if you use long attribute values, so yet again you would be better off using child nodes in this case
  • Obviously, you cannot use CDATA section in an attribute, the attribute value has to be properly quoted.

This post is part of You've asked for it series of articles.

How do I use xsl:choose

The Google query strings bringing visitors to my blog are sometimes amazing, like the one in the title of this post. Let's start with a tangential remark: if you're serious about XSLT, you should invest into a good book (XSLT Programmer's Reference is one of the best technical books I've ever read). If, on the other hand, you're just looking for a quick fix, here's the whole story

XSLT does not have if-then-else or select-case constructs like most other languages. You can implement both functions with the xsl:choose element that can take as many xsl:when children as needed. These are evaluated sequentially, the first one that succeeds is executed. You can also use xsl:otherwise as the last child in the list to specify the default behavior.


This post is part of You've asked for it series of articles.

Find processing instructions in XSLT

To get the value of a processing instruction from the input XML document in an XSLT stylesheet, use the /processing-instruction(name) function. I use a username PI in my XML-formatted ASP responses to pass the logged-in user parameters to XSLT, for example ...
<?user Web admin (admin@something.net) {00000000-0000-0000-0000-000000000000}?>
To get this string into an XSLT parameter, I use the following XSLT instruction:
<xsl:param name="username" select="/processing-instruction('user')" />

A processing instruction can appear anywhere in the XML document. To find processing instructions that are not children of the root element, use the appropriate XPath syntax.

You have to use cells array, not DOM children to select table cells

Recently, I wanted to use DOM property firstChild to select the first cell in a TABLE row ... and got quite unexpected results, the firstChild in my case was a text node. Once I've warmed up my grey cells, it all became quite obvious. For example, in the following HTML markup ...
<table>
  <tr id='test'>
    <td>Cell #1</td>
    <td>Cell #2</td>
  </tr>
</table>
... the first child of the TR element is the whitespace between the TR tag and the first TD tag. As the whitespace is allowed in that position by the HTML standard, you cannot rely on the TR.firstChild property. You should use TR.cells[0] to select the first cell.

Introduction to HIJAX

In my Introduction to HIJAX article recently published by InformIT, you'll find a hands-on approach to HIJAX methodology explained step-by-step on a sample application.

How do you write utf-8 data from ASP?

Obviously this is too simple, so it's hard to find an explicit answer in the online documentation:

  • To select the output encoding you want to use in the ASP script, you have to set the response.codepage property or change the per-page default with the @Codepage directive. To use utf-8 encoding, set the response.codepage to 65001.
  • The HTTP Content-type header should match the encoding you're using. Set the response.charset (second part of the content-type header) to "utf-8".
  • If you use META tag in your HTML to set the content-type or encoding attribute in the xml pseudo-instruction, these have to match the HTTP encoding as well. You don't have to specify utf-8 encoding in XML, as it's the default XML encoding.
  • Instead of setting response.codepage in every response, you can set session.codepage at the start of the session.
  • Important: If you use HTML forms, you have to set the @Codepage in all ASP scripts processing the forms or change the AspCodepage metabase property, otherwise the script might misinterpret the input data.

This post is part of You've asked for it series of articles.

Which XML encoding should I use?

The short answer is utf-8. The long answer goes along these lines:

  • utf-8 is the default XML encoding specified by the XML standard. If you use it, you don't have to define the encoding manually, thus reducing the chances of introducing errors. For example, some browsers get confused if the encoding specified in the xml pseudo-instruction is not the same as the one specified in the HTTP header.
  • All XML parsers are required to recognize utf-8 encoding. By using it, you don't risk any future compatibility issues. For example, some of my applications break on some installations of Internet Explorer on Vista as they use windows-1250 encoding. They work fine within IE on Windows XP, Firefox on Vista (and sometimes even with IE on Vista).
  • By using utf-8 you'll never encounter a character you cannot encode.

This post is part of You've asked for it series of articles.

You cannot disable output escaping in Firefox

This is probably well known to all experienced XSLT users, but if this will save a few readers the headaches I've had, it's well worth writing :) In Internet Explorer you can disable output escaping when using XSLT in the browser, thus being able to generate HTML tags from the text content of XML nodes. You cannot do the same in Firefox, as Firefox (or any Gecko-based browser) uses XSLT to transform input tree directly into output (DOM) tree, whereas XSLT transformation in Internet Explorer generates an intermediate string which is later re-parsed into HTML DOM tree.

Firefox does not generate any error when encountering the disable-output-escaping attribute (it's allowed by the XSLT standard) but simply ignores it and inserts the quoted < and > characters into the output stream.

CSV format specifications

If you ever need to know specifics about Comma-Separated-Values (CSV) format, this is probably as good as you'll get. More about using XSLT to generate CSV files in an upcoming post :)

Firefox document object lacks DOM properties in XSLT-generated pages

I started working on a keyboard-shortcut library and knew (somewhere in the hidden inaccessible depths of my mind) that I had a problem with document.body property a while ago. As it's required by the HTML DOM standard, I immediately suspected that it was an IE problem ... and chased that ghost for a while, until I've realized how wrong I was.

The buggy browser was Firefox: if you create a web page with local XSLT transformation (dictated by the xml-stylesheet instruction in the XML document returned from the server) in Firefox (up to at least 2.0.0.6), everything looks normal, but document.body is null and document.forms, document.links and other similar array are empty.

To make matters more confusing, all other HTML DOM calls work (it's a different bug from this one), so I had to substitute the links array with document.getElementsByTagName("A") and the body property with the document.getElementsById("body") (and used the id attribute on the body tag).

Testing individual XSLT features

The easiest way to test individual XSLT features is as follows:


  • Install SAXON;

  • Create a small XML document with structure that represents your input data;

  • Create a small XSL stylesheet focusing on features you want to test;

  • Use xml:output method='text' in the test stylesheet to produce the test results without XML garbage;

  • Perform the tests with SAXON;

  • Test the transformation in your target environment. If you're developing browser-side transformations, insert xsl-stylesheet pseudo-instruction in your XML document and open it with Internet Explorer and Firefox.

You can see how I've used this approach in this post.

Handling unexpected children in xsl:apply-templates

When you use xsl:apply-templates in a xsl:template, you might get extra text strings in the transformed results if your input XML data contains unexpected elements with text nodes (due to default XSLT template rules, the extra elements themselves and their attributes are ignored).

There are three ways you can handle this situation:

  • Write an XML schema and validate input data against it before the transformation;
  • List the children you expect in the xsl:apply-templates instruction;
  • Define a low-priority default template the does nothing.
For example, when the input XML document ...
<?xml version="1.0" ?>
<data>
  <a>A1</a>
  <b>B1</b>
  <c>Unexpected</c>
  <b>B2</b>
  <a>A2</a>
</data>
... is processed with the stylesheet ...
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="data">
  <xsl:apply-templates /></xsl:template>

<xsl:template match="a">
  A: <xsl:value-of select="text()" />
</xsl:template>

<xsl:template match="b">
  B: <xsl:value-of select="text()" />
</xsl:template>

</xsl:stylesheet>
... you get the string Unexpected in the transformed results. You could either list the children you expect in the xsl:apply-templates, for example ...
<xsl:template match="data">
  <xsl:apply-templates select="a|b" /></xsl:template>
... or you could define a default template that does nothing (overwriting the built-in default that outputs the child text nodes) ...
<xsl:template match="*" priority="-100" />

This post is part of You've asked for it series of articles.

Another IE bug: transformed plain text displayed as HTML

If you use browser-side XSLT transformation triggered with xsl-stylesheet pseudo instruction in the source XML document and set the output format to plain text with xsl:output method='text', you'd expect the results to be displayed as unformatted text in the browser window (similarly to what browser does when served a document with content-type: text/plain). Not surprisingly, Firefox behaves as expected, but Internet Explorer 7 renders the results as pure HTML (going as far as interpreting the start-of-tag characters).For example, when the following XML document ...
<?xml version="1.0" ?>
<?xml-stylesheet href="t1.xsl" type="text/xsl"?>
<data>
  <row>Sample</row>
</data>
... is transformed with this stylesheet ...
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:output method="text" />

<xsl:template match="row">
&lt;b&gt;<xsl:value-of select="text()" />&lt;/b&gt;
</xsl:template>

</xsl:stylesheet>
... Firefox displays the resulting text (<b>Sample</b>), but Internet Explorer displays Sample.

Yet another reason to use Saxon

Testing a sample stylesheet today, I've discovered that Saxon reports a lot of recoverable errors that are silently ignored by other XSLT implementations (MSXML or Gecko).For example, when faced with this stylesheet ...
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:template match="data">
  <xsl:apply-templates /></xsl:template>
 
<xsl:template match="a">
  A: <xsl:value-of select="text()" />
</xsl:template>
 
<xsl:template match="a">
  B: <xsl:value-of select="text()" />
</xsl:template>
 
</xsl:stylesheet>
... Saxon would report ambiguous rule match (even documenting the input element that caused the error and the source line number), whereas all the other XSLT translators would silently produce "best effort" results. In fact, the stylesheet had a typo, the last template should match the b element and I would have lots of problems detecting that with MSXML (for example).

Why would I use select='text()'

<xsl:value-of select='text() /> is used when you want to select the text embedded between the opening and closing tag of the current element. However, in this context, the text() function selects only the first child text node. If you want to:

  • select all embedded text (including text within the child elements), use <xsl:value-of select='.' /> as this renders the whole tree of descendants as a text string;
  • select all text nodes within the current element but no text in descendant elements, use a <xsl:for-each select='text()' > loop.
For example, the following XML document ...
<?xml version="1.0" ?>
<?xml-stylesheet href="t1.xsl" type="text/xsl"?>
<data>
  <row>Before <b>an embedded tag</b> and after it</row>
</data>
... transformed with this stylesheet ...
<?xml version="1.0" encoding="windows-1250" ?>
<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:output method="text" encoding="windows-1250" />

<xsl:template match="row">
  Text(): <xsl:value-of select="text()" />
  Current element: <xsl:value-of select="." />
  Text loop: <xsl:for-each select="text()"><xsl:value-of select="." /></xsl:for-each>
</xsl:template>

</xsl:stylesheet>
... results in the following text:

Text(): Before
Current element: Before an embedded tag and after it
Text loop: Before and after it

This post is part of You've asked for it series of articles.

XSL: Remove whitespace from source XML document

While searching for a solution to a completely different problem, I've stumbled across a very elegant way to remove whitespaces from input XML documents before they are transformed (see also description of problems caused by them) - use xsl:strip-space instruction, for example <xsl:strip-space elements='*' />

Test for empty attributes in XSLT

Testing for missing attributes in XML elements is different from testing for attributes with empty values. You can test for missing attributes with <xsl:if test="not(@attribute)">, but this test will never succeed if the attribute is present but empty. In that case, you have to use the <xsl:if test="@attribute = ''"> condition.

This post is part of You've asked for it series of articles.

Workaround: generate explicit closing tag in XSLT

It looks like the PHP XSLT functions have a bug when generating HTML output: they generate self-closing <script src="..."/> tags that cause Internet Explorer to hickup, although the XSLT transformation engines should take care of HTML peculiarities (including the set of <script></script> tags) when you set xsl:output to html.

The workaround provided in PHP documentation comes handy in any situation where you want to have an explicit closing tag for whatever reason. Instead of <tag></tag> which is usually output as <tag />, your XSLT transformation should contain <tag><xsl:comment /></tag>, which will generate the opening and closing tag.

Output XML data from ASP

These are the steps you need to take to output XML data from ASP script:

  • Clear the output buffer to ensure no HTML tags are sent to the client;
  • Set the content-type to text/xml;
  • Optionally (but highly recommended) set the character set to utf-8 and change the ASP code page;
  • Output the XML data (for example, writing a DOM object xml property to the output stream);
  • Stop ASP script processing.

The sample ASP code is included below and you can get more in-depth description of the necessary operations in my InformIT article Optimized presentation of XML content.

Response.Clear
Response.ContentType = contentType
Response.Charset = "utf-8"
Response.Codepage = 65001
Response.Write txt
Response.End

Note: this post is part of You've asked for it series of articles.

Converting forms to AJAX

Form submission handling is probably one of the more challenging topics if you've decided to deploy an AJAX application instead of a traditional page-by-page web application. In his short article, Kris Hadlock has provided an excellent sample code that you can deploy to transform a form submission request into an AJAX request (thus retaining tight control over the results and error handling).

How to avoid common CSS mistakes

I admit it - I was also one of those web developers that jumped in CSS waters with helping hand of uncle Google instead of a good introductory and reference book on the topic. If you want to avoid some of the stupidities I've been doing, read the Seven Common CSS Mistakes and How to Avoid Them article I wrote for InformIT.

Using transformNode in IE7

Apparently a lot of people land on my blog when using Google to figure out how to use transformNode function in IE7. The answer is simple - you don't do it, as it's IE-only function that will obviously not work in any other browser.

What you should do instead is to use a wrapper library like Sarissa that gives you XSLTProcessor object in both Firefox and Internet Explorer environments. The XSLTProcessor API is described in Mozilla documentation (which also includes a simple example).

Client-side development environment

Scenario: XSLT deployment in a browser/server scenario with XSLT transformations performed on the browser (JavaScript or with <?xsl-stylesheet ?>)

Potential tools:

  • Saxon is great for off-line testing (primarily while developing small bits of code or trying new programming techniques). It cannot fetch the information from the server, so you have to provide test data in local XML files
  • Firefox is the best tool if you want to test client-server integration and use <?xsl-stylesheet ?> approach as it can recover from XSLT errors (which IE can't). However, it doesn't detect all errors, so the stylesheets working with Firefox might break in IE (but not due to an IE bug)
  • IE is a nightmare when using <?xsl-stylesheet ?> directive. Whenever something breaks (error in stylesheet or even server-side error resulting in HTML response instead of XML response), its cache gets messed up and you have to restart the browser to continue the tests. I therefore use IE only in the final tests to verify that my XSLT has no weird errors not detected by Firefox.
  • The best testing mechanism is probable the manual (JavaScript) client-side transformation. You can do it in IE or Firefox; in this case I would prefer IE, as it has better XSLT error reporting (MSXML is not that bad :).

Example: Use XSLT transformation in browser

Several readers were asking for an code sample that performs XSLT transformation in the browser, so here it is :) To start with, you should use a wrapper library to isolate yourself from browser version/platform inconsistencies. I'm using Sarissa to deal with XML, XSLT and HttpRequest and X library to deal with DHTML-related functions.

The most common XSLT-related operation that you do in your browser is this:

  • Download XML document from the web server using XMLHttpRequest;
  • Transform the XML into application-specific HTML markup with an XSLT transformation;
  • Insert the resulting HTML markup into the web page.

Sarissa library has a function (Sarissa.updateContentFromURI) that does almost exactly what you need to get there, so you only need to perform the following steps:

  • Load the XSLT stylesheet as XML document (sync load is easier to do).
  • Import the loaded XML document into an XSL processor (special object that can perform the XSLT transformations).
  • Call the updateContentFromURI function.

The resulting code is very simple:

// load XSLT stylesheet, done only once.
var xsl = loadURI("moveRoutes_lists.xsl");
if (!xsl) throw("Cannot load XSL stylesheet");

var xform = new XSLTProcessor();
xform.importStylesheet(xsl);

// the following call transforms a remote XML document
// into HTML fragment and inserts it into the target
// element
Sarissa.updateContentFromURI(remoteURI, xGetElementById(targetElement),xform);

Notes:

  • sarissa.js as well as sarissa_dhtml.js libraries have to be included in the source document.
  • xGetElementById function is part of the X library.
  • loadURI function is documented here.

JavaScript progressive enhancement in practice

In the JavaScript Progressive Enhancement in Practice article I'm describing the progressive enhancement philosophy and techniques through a simple example: frequently asked questions page.

Server-side XSLT transformations

If you need to transform XML with XSLT on the server, you could use MSXML on Microsoft platforms (ASP) or built-in XSL functions in PHP 5. The beauty of the PHP functions is that they provide the same API as the Firefox browser and the Sarissa library, so you don't have to reinvent the wheel.

I would often use XSLT on the server to separate data (XML) from its presentation. Its usage also results in cleaner server-side code, as the markup generation is separated from the programming logic. It's also extremely useful if you want to deploy XML/XSLT-based solution that has to support low-end clients (for example, search engine spiders).

Error handling Sarissa wrapper

In a previous post I've included a simple Sarissa wrapper that could be used to load an XML document (for example, XSLT stylesheet). If you want to perform more error checking and generate sensible errors, you could use these two functions:

  • loadFromServer function requests object load. If you provide a callback parameter, the load is asynchronous, otherwise it waits for the load to complete and return the wrapper object.
  • checkLoadStatus checks the various error conditions and throws appropriate errors.
var AWS = new Object();

AWS.loadFromServer = function (url,cb) {
var lso = new Object() ;

function loadHandler() {
if (lso.request.readyState == 4) cb(lso); }

lso.savedURL = url ;
lso.request = new XMLHttpRequest() ;
lso.request.open("GET", lso.savedURL, cb ? true,false);
if (cb) lso.request.onreadystatechange = loadHandler;
lso.request.send(null);

return lso;
}
AWS.checkLoadStatus = function (lso) {
var url = lso.savedURL ;
var xr = lso.request ;
var msg;

if (!url) throw("AWS.checkLoadStatus expects loadFromServer object") ;
if (xr.status != 200) throw (url + " failed: "+xr.statusText);
try {
var de = xr.responseXML.documentElement;
if (!de) msg = url + ": response is not a valid XML document";
} catch(err) {
msg = url + ": response is not a valid XML document (" + err + ")";
}
if (msg) throw(msg);
return xr.responseXML;
}

Offline XSLT processor

If you need an off-line XSLT processor, nothing beats SAXON. It supports XSLT 1.0 and XSLT 2.0 and the lower-end version is free. I am strictly using the 1.0-only version to ensure my code is compliant with major browsers, but you might want to get the 2.0-version for special projects.

I'm primarily using SAXON to test my XSLT code (or develop new programming techniques) before deploying it in a distributed environment. I was also using it to develop the WordML transformations that are now executed within the context of Save As ... function in Word.

Simple high-level wrapper for Sarissa

In some cases, Sarissa calls are too low-level for my needs. For example, sometimes I need a function that would load an XML document synchronously and just throw an error if something fails. Here it is:
function loadURI(uri,async) {
var d = Sarissa.getDomDocument();
d.async = false;
d.load(uri);
if (d.parseError) throw("Error loading "+uri+": "+Sarissa.getParseErrorText(d));
return d;
}

Make pop-up windows visible to search engines

The article Make Pop-Up Windows Visible to Search Engines shows you how to open the pop-up windows from your web pages (although that's more and more often considered a bad practice that should be limited to special cases) while still retaining links to the pop-up content that can then be discovered by search engines.

Recommended wrapper libraries

In the last year, I've settled on two JavaScript browser wrapper libraries that satisfy almost all my needs to achieve browser version/platform independency: Sarissa handles XML, XSLT and XMLHTTPRequest and the X library handles a large number of DHTML- and DOM-related functions.

Note: I decided not to use a framework library. If you want to go down that route, these two would be way too low-level.

Automate the pagination of web pages

A lot of articles of the web (for example, this one) document how you can present a web page as a series of screen-sized pages. In most cases, the page author has to provide the pagination (in form of DIV elements, for example). In the InformIT article Automate the Pagination of Your Web Pages, I'm describing a method of generating the pages from linear text automatically in the browser, based on (for example) heading elements.

Using WordProcessingML to Generate Clean HTML from Word

Generating clean and simple HTML output from Microsoft Word is very difficult if you rely on its "Save As Web Page" feature. In my InformIT article, Using WordProcessingML to Generate Clean HTML from Word, I'm describing how you can use WordProcessingML together with XSL transformations to generate clean, strict (X)HTML-compliant documents from Word sources.

MSXML is still only DOM-1 compliant

If you use DOM calls to manipulate XML documents returned with XMLHttpRequest object (and who doesn't), you'll be sooner or later highly upset when the code you've tested on Firefox or Opera fails in IE (including IE7). The reason - Microsoft still did not implement Node.getElementById function. So here's the "replacement":
function getDomById(e,id) {
if (e.getElementById) { return e.getElementById(id); }
if (e.documentElement) e = e.documentElement;

var cn = e.childNodes;
for (var i = 0 ; i != cn.length; i++) {
var n = cn[i];
if (n.nodeType == 1) {
if (n.getAttribute("id") == id) return n ;
}
}
for (var i = 0 ; i != cn.length; i++) {
var n = cn[i];
if (n.nodeType == 1) {
var result = getDomById(n,id); if (result) return result;
}
}
return false;
}
Note: The procedure is doing breadth-first search as I was looking for IDs that were pretty high in the hierarchy of trees with many branches.

Make Pop-Up Windows Visible to Search Engines

In the InformIT article Make Pop-Up Windows Visible to Search Engines I'm describing how you can make content in pop-up windows visible to search engines and accessible to visitors without JavaScript support.

Optimized presentation of XML content

In the Inform-IT article Optimized Presentation of XML Content I'm describing how you can serve XML content in its raw form to XSLT-compliant browsers (which do the transformation into HTML locally) while generating HTML pages from the same content for non-XSLT browsers (for example, early releases of Opera) and the search engines.

Firefox: HTML DOM fails if a local XSL transformation does not specify xsl:output

This nasty beast took me a while to figure out: if you do local XSL transforms in Firefox 1.5 and don't have the xsl:output directive in your main XSL stylesheet, HTML-specific DOM calls might not work.

The results are weird: for example, whatever is produced with the XSL transformation behaves like a HTML DOM object, but if you create a new element, it lacks HTML-specific DOM calls.

And the obvious solution: make sure you're always including ...
<xsl:output method="html" encoding="utf-8"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
... in your XSL stylesheet.

IE7 bug: addEventListener does not work with table row mouseover event

If you want to implement your JavaScript code using DOM-compliant events, you'd be unpleasantly surprised to learn that table rows in Internet Explorer do not respond to mouseover events attached to them with addEventListener. Firefox and Opera work as expected. The only way to achieve cross-browser compatibility is to use the old onmouseover properties: tablerow.onmouseover = myfunc; instead of tablerow.addEventListener("mouseover",myfunc).

Firefox 1.5 bug: table.rows property is empty when using XSL

If you generate a table in Firefox 1.5 with an XSL stylesheet within the browser from an XML document loaded from the server, the table's rows property will contain no rows. You can still get the rows using the childNodes property though.

To make matters worse, Firefox does not generate an implicit TBODY element in the DOM tree, but IE7 and Opera do. Makes for a nice nightmare if you aim at cross-browser support ... the workaround code is included below:
function recurseChildNodes(parent,tag,callback) {
if (! parent.nodeName) return;
if (parent.nodeName.toLowerCase() == tag) { callback(parent); return; }
if (! parent.childNodes) return;

for (var i = 0; i < parent.childNodes.length; i++) {
recurseChildNodes(parent.childNodes[i],tag,callback);
}
}


recurseChildNodes(table,"tr",callbackFunction);