Showing posts with label HTML. Show all posts
Showing posts with label HTML. Show all posts

Custom data in HTML5 tags

Quite often I’d like to add application data to my HTML markup to pass information between server-side scripts generating the HTML markup and client-side jQuery scripts. Prior to HTML5 you could decide to use XHTML and your private namespace; numerous applications (including Facebook and my web sites) use this approach. It works nicely unless you’ve decided to use client-side XSLT transformation in Firefox.

HTML5 gives you another option: embedding custom non-visible data in HTML tags. You can add as many attributes as you wish to a HTML tag as long as they start with the data- prefix. HTML5 compliant browsers will eventually give you DOM access to these attributes through the dataset attribute; in most browsers (ancient IE or Netscape releases might not work) you can get these attributes with the getAttribute (or jQuery attr) call.

You might wonder what the difference is between using non-standard attributes of your choice and standard data- attributes as long as the browsers don’t support the dataset property. If nothing else, your HTML code will be validated by HTML5 validator.

Firefox 3.5 XHTML support stinks

Continuing my Firefox “quality” rants: I just found the willpower to completely redesign my AJAX framework, going from IFRAMEs to jQuery AJAX calls (replacing straightforward and quite elegant XSL transformations with pages of convoluted JavaScript code) to work around the bugs in FF3.0, only to find out that FF3.5 is even worse and introduced numerous additional “features”.

For example, FF3.5 requires an explicit </script> tag within an XHTML document with proper DOCTYPE. The following document was validated with W3C XHTML validator …

<!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/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Test</title>
  <script src="… source URL … " type="text/javascript" />
</head>
<body id="body">
</body>
</html>

… but it does not load the script in FF3.5.

Before anyone starts telling me that it’s not so hard to include the closing </script> tag in your source – try telling that to the database with native XML support where I’m storing snippets of the code. I had to convert the field from XML to TEXT (and lose all XML goodies I might eventually get) just to avoid the elimination of the explicit closing tag.

May I make a quick suggestion to the FF developers: maybe, just maybe, you might want to consider supporting existing web applications in parallel with adding new features that not too many people can use (because no other browser supports them yet).

Refactoring a simple menu

Years ago I had to implement a drop-down menu. Nothing fancy; no open-on-hover magic, but a simple line of buttons with drop-down boxes that would open on clicking the button.

There were just a few annoying details: clicking a top-row button should obviously open a drop-down box, but also change state the button’s state to “depressed” and close any other open drop-down box (and change the state of corresponding buttons).

Here is my five-year-old HTML code …

<div style="float: left; position: relative; z-index: 100;" id="IDAHYJQB">
  <p class="btn170">
    <a href="/climbing/myClimbs/myClimbs.asp">First page</a>
  </p>
</div>
<div style="float: left; position: relative; z-index: 100;" id="IDAKYJQB">
  <script>menuRegister('IDAKYJQB')</script>
  <p class="btn170" id="IDAKYJQB_main">
    <a href="javascript:menuClick('IDAKYJQB')">Add ...</a>
  </p>
  <div style="position: absolute; display: none;" id="IDAKYJQB_sub">
    <p class="btn170">
      <a onclick="menuSelect('IDAKYJQB')" 
        href="/climbing/myClimbs/myClimbs_add.asp">New entry </a>
    </p>
    <p class="btn170">
      <a onclick="menuSelect('IDAKYJQB')" 
        href="/climbing/myClimbs/myClimbs_editWall.asp?a=add">Edit</a>
    </p>
    … more …
  </div>
</div>

… the corresponding CSS …

.btn170, .btn170_down { background-repeat: no-repeat; 
    width: 170px; height: 20px; line-height: 18px; overflow: hidden; 
    padding: 0 0; margin: 0 0; text-align: center;
    font-family: Verdana, Arial, Helvetica, sans-serif; 
    font-weight: 700; font-size: 11px; }

.btn170
  { background-image: url('images/button_170.gif');  }   
.btn170_down
  { background-image: url('images/button_down_170.gif'); }

… and JavaScript code …

var topMenuItems = [] ;

function addClass(id,sfx) {
  var se = getElement(id) ;
  if (se.className.indexOf(sfx) < 0) se.className = se.className + sfx ;
}
function removeClass(id,sfx) {
  var se = getElement(id) ;
  var i = se.className.indexOf(sfx) ;
  if (i > 0) se.className = se.className.substr(0,i) ;
}

function menuShow(id) {
  var se = getElement(id) ; se.menuActive = true ; 
  showElement(id + "_sub") ; addClass(id+"_main","_down"); }

function menuHide(id) { 
  var se = getElement(id) ; se.menuActive = false ; 
  hideElement(id + "_sub") ; removeClass(id+"_main","_down"); }

function menuGo(id,l) { menuHide(id); location.href = l; }
function menuSelect(id) { menuHide(id) ; }

function menuClick(id) {
  var i,se ;
  se = getElement(id) ;
  if (se.menuActive) {
    menuHide(id) ; return ;
  }
  for (i = 0 ; i < topMenuItems.length ; i++) {
    if (topMenuItems[i] != id) menuHide(topMenuItems[i]);
  }
  menuShow(id) ;
}

function menuRegister(id) { 
  topMenuItems[topMenuItems.length] = id ;
}

I will not try to explain what this code does, as it’s way too painful. As I’ll walk through the refactoring process, I’ll show you the changes I’ve made and the stupidities in the original code.

XSLT transformation in ASP: HTML with MSXML6

If your IIS platform includes MSXML6, you should use MSXML6 to transform XML into HTML instead of MSXML3 (see the Using the right version of MSXML article for proper fallback process). MSXML6 still generates the META tag (the MSXML3-related post describes problems caused by the META tag), but does not include the charset parameter in it, resulting in perfect HTML output. The sample ASP program using the simple XSLT transformation from the “XSLT transformation in ASP: HTML with MSXML3” post produces the following output when using MSXML6 (MSXML2.DomDocument.6.0 ProgID):

<html>
<head>
<META http-equiv="Content-Type" content="text/html">
<title>Sample XSLT server-side transformation</title>
</head>
<body><node type="test">Greek letter: β EE: č</node></body>
</html>

If you want to use complex XSLT transformations with MSXML6, you might have to set additional second-level DOM properties, for example AllowDocumentFunction. You might also want to set ValidateOnParse to false if the validity of your XML document is not a major concern.

XSLT transformation in ASP: HTML with MSXML3

Most commonly, you’ll use server-side XSLT transformation in ASP to transform XML data into HTML using MSXML3 (available on almost all IIS platforms). The following XSLT stylesheet is used in the HTML test:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
<xsl:output method="html" encoding="utf-8" />

<xsl:template match="root">
  <html>
    <head>
      <title>Sample XSLT server-side transformation</title>
    </head>
    <body>
      <node type="test">
        Greek letter: <xsl:value-of select="@greek" /> 
        EE: <xsl:value-of select="@ee" />
      </node>
    </body>
  </html>
</xsl:template> 

</xsl:stylesheet>

With MSXML3, the transformNode function inserts a META directive in the output stream, resulting in the following transformation result:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-16">
<title>Sample XSLT server-side transformation</title>
</head>
<body><node type="test">
Greek letter: β 
EE: č
</node>
</body>
</html>

On the other hand, the HTTP headers generated by the test ASP script claim the content is UTF-8 encoded (and the raw data dump performed with Fiddler confirms that). The response headers were taken from LiveHTTPHeaders Firefox extension:

HTTP/1.x 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 15 Sep 2008 11:20:56 GMT
X-Powered-By: ASP.NET
Content-Length: 222
Content-Type: text/html; Charset=utf-8
Cache-Control: private

Most browsers (including IE and Firefox) take the Content-type from HTTP headers, using the META directive as a fallback. They thus render the page as intended. Some browsers (including the TextView tab of Fiddler) prefer the META directive and produce garbage.

The solution

To match the META header with the HTTP headers, replace the charset=UTF-16 string in the text returned by the transformNode function with charset=UTF-8. The modified sample ASP script is included below:

<%
Const DOMClass = "MSXML2.DOMDocument"

Set XSLT = Server.CreateObject(DOMClass)
Set XDoc = Server.CreateObject(DOMClass)

XDoc.loadXML("<root greek='β' ee='č' />")
XSLT.load(Server.MapPath("SampleXSLT.xsl"))

Response.Clear
Response.Charset = "utf-8"
Response.Codepage = 65001
Response.Write Replace(XDoc.transformNode(XSLT),"charset=UTF-16","charset=UTF-8")
%>

Chrome: first impressions

I've just downloaded Chrome (the new browser from Google), tested it on my applications and got immediately impressed - they all worked, even the client-side XSLT transformation driven by the xsl-stylesheet directive. Then I did a few more random tests and my enthusiasm was drastically reduced:
  • The inter-line spacing on table heading texts was way too large: <th valign='bottom'>Line 1<br />Line 2</th> produced a blank line between the two text lines. I did not investigate what the root cause might be as all the other major browsers render it almost identically, so I don't really care what upset Chrome.
  • The top line of our corporate Wiki (the Login text) is misplaced.
  • The View source window does not display processing instructions in XML documents.
  • And the worst offender: Blogger in draft works way better, faster and more reliable in Firefox or Internet Explorer than in Chrome.
Summary: I will have Chrome installed to test my applications (some visitors are already using it), but will not use it in the near future.

Multiple style attributes in IE and FF

I've just stumbled across an interesting discovery today: if you use the style attribute multiple times in a single HTML tag (which you should not do, BTW, but it could happen if you write HTML code by hand), Internet Explorer will merge the style definitions whereas Firefox will ignore the second style attribute.

Drawing charts in your web pages

Here are a few links that will help you draw great charts in your web pages:

Query-string-based revision control

One of the easy ways to improve the perceived response time of your web site is to ensure that your web server sets explicit Expires: HTTP header on the static web page components (JavaScript libraries, CSS files, images …), thus reducing the number of HTTP requests sent by the browser. However, if you change your JavaScript code or CSS, your visitors could be stuck with the old version for a long time.

If you use static HTML and a decent development environment, you can easily rename the JavaScript or CSS files (and the HTML pages get updated as a side effect). For more complex environments, you could use an easy trick: append the revision number as a query string after the file name:

<link href="myStyle.css?57” rel="stylesheet" type="text/css" />
<script src="myCode.js?42" type="text/javascript"></script>

Most web servers ignore the query string after the name of a static file, but the browsers perform the caching on whole URL (so x.js?1 is different from x.js?2).

Analyze your web page peformance

Straight from the Yahoo Developer Network: YSlow analyzes any web page and generates a grade for each rule and an overall grade. If a page can be improved, YSlow lists the specific changes to be made. Highly recommended tool :)

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.