Version 10, last updated by edgarchan at 19 Jan 19:18 UTC

HtmlProperties, XHTML and HTML5

Lift unifies many aspects of parsing and displaying the HTML page in a single trait,HtmlProperties.HtmlProperties defines, on a session-by-session (and even a request-by-request) basis, the way that templates are parsed and the way that Scala’sNodeSeq is converted into valid HTML output. The properties on HtmlProperties are:

  • docType – the DocType for the HTML page, e.g.,<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> or <!DOCTYPE html>
  • encoding – the page’s encoding, e.g.,<?xml version="1.0" encoding="UTF-8"?>
  • contentType – the setting of the Content-Type response header, e.g.,application/xhtml+xml; charset=utf-8ortext/html; charset=utf-8
  • htmlOutputHeader- calculates the way to combine the docType and encoding(this is important for IE6 support where encoding goes after docType).
  • htmlParser – a function that converts anInputStreamto aBox[NodeSeq]. This is used by Lift to parse templates.
  • htmlWriter- a function that writes aNodeSeqto aWriter. This is used by Lift to convert the internal XML representation of a page to a stream of bytes representing an HTML page.
  • html5FormsSupport – a flag indicating whether the current browser supports HTML5 forms.
  • maxOpenRequests – the maximum number of concurrent HTTP requests the browser supports to a named host.
  • userAgent – the User-Agent string sent from the browser.

XHTML viaOldHtmlProperties

The default properties that keep compability with the disparate LiftRules used to calculate DocType and Encoding. Uses thePCDataXmlParserparser which requires well-formed XML files. Output is generally XHTML viaAltXML.toXML, but cerain tags (e.g.,<br>) are written in IE6/IE7 friendly ways.

HTML5 viaHtml5Properties

Prior to Lift 2.2, Lift always emitted XHTML and by default set the Content-Type header toapplication/xhtml+xml; charset=utf-8. This continues to be Lift’s default behavior. It turns out that most browsers, even modern ones (Firefox, Chrome and Safari) had issues with XHTML. Further, XHTML limited the behavior of certain JavaScript libraries. By invoking LiftRules.htmlProperties.default.set((r: Req) =>new Html5Properties(r.userAgent)) in Boot.scala, you can set Lift to full HTML5 support.

Lift uses the nu.validator HTML parser. This parser tries to fix broken HTML and will do so rather than throwing an exception. This sometimes causes confusion, especially when converting from XHTML or using older example code. If you’re using Html5, read this page completely before posting to the Lift mailing list about stuff that’s not working… especially the part about snippet invocation syntax. Also, please note that we are using this library, but have no control over it, so we can’t change its behavior.

Lift emits the correct DocType and response headers such that all tested browsers (IE6+, Firefox 2+, Safari 2+, Chrome 1+) render tags correctly (e.g., not putting a closing tag on void elements). Because the HTML5 parser is different from the standard XML parser, you will need to adjust your existing templates in the following ways:

  • All tags and attributes are converted to lower case. This means the <lift:FooBar isSilly=“true”></lift:FooBar> gets converted to <lift:foobar issilly=“true”/> I advise converting to designer friendly where possible (e.g.,<div class="lift:FooBar?isSilly=true"></div>).
  • Tags of the format<div/>and<my_thing:bind/>are not legal. They must be converted to <div></div> and <my_thing:bind></my_thing:bind>. Unfortunately, the parser is very forgiving so rather than barking about the lack of closing tag, the parser will nest things in unexpected ways or discard the offending tag without any notification.
  • There are some tags that the parser “ensures”. For example a <tr>,<thead>, or <tbody> tag must be the first tag inside <table>. This breaks the
    <table>
      <mysnippet:line>
        <tr>
          <td>
            <mysnippet:bind_here></mysnippet:bind_here>
          </td>
        </tr>
      </mysnippet:line>
    </table>
    paradigm. You can get the desired behavior with
    <table>
      <tr lift:bind="mysnippet:line">
        <td>
          <mysnippet:bind_here></mysnippet:bind_here>
        </td>
      </tr>
    </table>

head merge in HTML5

Since the parser strips any head tag not directly following an html tag, you need to use the head_merge tag if your templates contain head elements that should be merged into the final pages’s head element.


    <div id="main" class="lift:surround?with=default&at=content">

        <head_merge>
            <title>My page title</title>
            <script>
                $(function(){
                   $('#dummy').click(function(){ alert('foo'); });
                });
            </script>
        </head_merge>
    
        <button id="dummy">ok</button>

    </div>

Changing behavior mid-session or mid-request

for {
  session <- S.session
} session.requestHtmlProperties.set(session.
          requestHtmlProperties.is.setDocType(() => 
                                   Full("<!DOCTYPE moose>")))