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 ...
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"... in your XSL stylesheet.
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
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:
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);
Web caching, Part 3: Database Integration
The last article in my Web caching series published by InformIT.com details the database integration methods. As most SQL databases don't give you record modification timestamps, you have to implement them yourself. In this article, you'll see how you can use SQL triggers to implement table-level, record-level and join-level timestamps that can be used to set the last-modified date on an HTTP response.
XSL: create optional class attributes in HTML output
When generating styled HTML output from XML, you might need to attach a special class to the first and/or the last element in a series (for example, first and last child). An only child would obviously have both classes attached to it (don't forget, class attribute can list multiple classes separated by a whitespace). The code to generate optimal list of HTML classes (or doing a generic join of list elements) is pretty simple:
<xsl:variable name="class">Please note the following:
<xsl:if test="position() = 1"> FirstInSeries</xsl:if>
<xsl:if test="position() = last()"> LastInSeries</xsl:if>
</xsl:variable>
<xsl:if test="$class != ''">
<xsl:attribute name="class">
<xsl:value-of select="substring-after($class,' ')" />
</xsl:attribute>
</xsl:if>
- Each value in the list has a leading whitespace. The list values are thus properly separated, but the result has an extra leading whitespace.
- The last xsl:if instruction tests if the class attribute is needed (detailed description of this trick).
- The substring-after function removes the leading whitespace, resulting in the desired value for the class attribute.
XSL: avoid empty attributes in output stream
Whenever you create an attribute with an xsl:attribute instruction containing conditional instructions (xsl:if or xsl:choose), the result might be empty, resulting in an empty attribute in the output stream. For example, both my simplistic WordProcessingML to HTML stylesheet or similar code published on OpenXML Developer could create <span style=""> elements.
To avoid this problem, capture the conditional results into a variable and create the attribute only if the variable is non-empty, for example:
To avoid this problem, capture the conditional results into a variable and create the attribute only if the variable is non-empty, for example:
<xsl:variable name="style">
<xsl:if test="w:rPr/w:i">font-style: italic;;</xsl:if>
<xsl:if test="w:rPr/w:b">font-weight: bold;</xsl:if>
</xsl:variable>
<xsl:if test="$style != ''">
<xsl:attribute name="style"><xsl:value-of select="$style" /></xsl:attribute>
</xsl:if>
XSL: transform an element only if it's a descendant of another element
I recently had to work on WordProcessingML documents and wanted to transform only those w:p elements that were within the subtree of the w:body element. My initial solution was "a bit" complex: match all w:p elements that have a w:body ancestor.
<xsl:template match="w:p[ancestor::w:body]">There is (as always) a much more elegant solution:
<xsl:template match="w:body//w:p">
XSL: Detect first-of-type element in a list
Sometimes you want your XSL transformation to process first element of a type in a child list in different manner. For example, using the following data ...
(A) Write two transformation rules, one for the first element, one for the remaining ones:
<?xml version="1.0" encoding="UTF-8" ?>... you might want to process the first editor in a slightly different manner. There are two simple solutions:
<list>
<author>John Brown</author>
<author>Jim Small</author>
<editor>Jane Doe</editor>
<editor>Grace Kelly</editor>
</list>
(A) Write two transformation rules, one for the first element, one for the remaining ones:
<xsl:template match="editor[1]">(B) Use preceding-sibling axis to check whether an element is the first of its type:
<!-- transform the first editor in list -->
</xsl:template>
<xsl:template match="editor">
<!-- transform the remaining editor elements -->
</xsl:template>
<xsl:template match="editor">You should use the first method when the handling of the first element is radically different from the rest and the second one when you only need to set a few attributes or write a lead-in text.
<xsl:if test="not(preceding-sibling::editor)">Editors:</xsl:if>
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
IE7 breaks some AJAX libraries
Probably this is very old news (and Internet is full of related blog entries), but somehow I've managed to miss it ... If you use XMLHTTP Request object and initialize it similar to this:
Note: If you use Sarissa (which broke my AJAX application), download the latest version, it contains all relevant IE7 fixes.
if (IE) {
x = new ActiveXObject("Microsoft.XMLHTTP")
} else {
x = new XMLHttpRequest();
}... Internet Explorer 7 will constantly complain about the page trying to use ActiveX controls (at least IE6 was silent unless you've disabled ActiveX in which case AJAX broke anyway). The proper way to deal with this particular quirk of IE is to test for window.XMLHttpRequest first (instead of relying on browser type) and use ActiveX only if needed.Note: If you use Sarissa (which broke my AJAX application), download the latest version, it contains all relevant IE7 fixes.
XPath position() function returns unexpected results
If you process XML documents generated "manually" (including documents composed within a program without proper XML tools), you might be surprised by the results returned by with position() function. For example, the input XML document ...
<?xml version="1.0" encoding="UTF-8" ?>... processed with a simple XML stylesheet ...
<list>
<author>John Brown</author>
<editor>Jane Doe</editor>
<author>Jim Small</author>
<editor>Grace Kelly</editor>
</list>
<?xml version="1.0" encoding="utf-8" ?>... returns surprising results:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:template match="list">
Results: <xsl:apply-templates />
</xsl:template>
<xsl:template match="author|editor">
<xsl:value-of select="text()" /> (<xsl:value-of select="position()" />)
</xsl:template>
</xsl:stylesheet>
Results:The reason for this unexpected behavior are whitespace text nodes between the author and editor elements which are also counted by the position() function. To skip them, either create XML documents without extra whitespace or use more specific xsl:apply-template statements, for example:
John Brown (2)
Jane Doe (4)
Jim Small (6)
Grace Kelly (8)
<xsl:template match="list">The select="*" option in the last example selects only child nodes of the current XML node and thus skips over text fragments (including whitespace nodes).
Results: <xsl:apply-templates select="*"/>
</xsl:template>
Web Caching, Part 2: Reduce the Download Time
In my second article in the Web caching series published by InformIT.com, I've focused on caching dynamically generated pages in the browser cache. The article explains the in-depth details of browser-side HTTP caching and the HTTP headers you have to process in server scripts to make your dynamic pages cacheable.
When properly implemented, this solution can drastically reduce the amount of information downloaded to visitor's browser, thus increasing the overall responsiveness of your web application.
When properly implemented, this solution can drastically reduce the amount of information downloaded to visitor's browser, thus increasing the overall responsiveness of your web application.
Web Caching, Part 1: Explicit Content Expiration
InformIT.com has published Reap the Benefits of Web Caching, Part 1: Explicit Content Expiration; the first article in my three-part series covering usage of web caching mechanisms in dynamic web pages.
Flash and Search Engines
This article gives you an excellent idea how to use Flash in your web sites while still having them completely visible to the search engines.
Subscribe to:
Posts (Atom)