Search code examples
javascriptjsfjsf-2xhtmlfacelets

Error parsing XHTML: The content of elements must consist of well-formed character data or markup


As an extension of this question, I'm trying to insert Javascript to a <h:commandButton />'s onclick property as action is already rendering an ajax table.

What I want to do: Get the selected items in a list box and turn them into parameters to be used in a JSF FileServlet. i.e. para2=value1&param=value2&param=value3

Here's what I have:

<script type ="text/javascript">
function myScript() {
    var box = document.getElementbyId('myForm:box');
    var length = box.options.length;
    var paramstring = "";
    for (var i = 0; i < length; i++) {
        if (i != (length - 1) {
            if (box.options[i].selected) {
                paramstring = paramstring + "param=" + box.options[i].value + "&amp;";
            }
        } else {
            paramstring = paramstring + "param=" + box.options[i].value;
        }
    }
    if (document.getElementById('myForm:checkbox').checked) {
        window.location='fileServlet? + paramstring;
    }
}
</script>  

What I get when page is loaded: javax.servlet.ServletException: Error Parsing /page.xhtml: Error Traced[line:15] The content of elements must consist of well-formed character data or markup.

What doesn't trigger exception:

<script type ="text/javascript">
function myScript() {
    var box = document.getElementbyId('myForm:box');
    var length = box.options.length;
    var paramstring = "";

    if (document.getElementById('myForm:checkbox').checked) {
        window.location='fileServlet? + paramstring;
    }
}
</script> 

As soon as I add in for (var i = 0; i < length; i++) or even for (var i = 0; i < 10; i++) the page wouldn't load. Why does it not like the for loop?


Solution

  • Facelets is a XML based view technology which uses XHTML+XML to generate HTML output. XML has five special characters which has special treatment by the XML parser:

    • < the start of a tag.
    • > the end of a tag.
    • " the start and end of an attribute value.
    • ' the alternative start and end of an attribute value.
    • & the start of an entity (which ends with ;).

    In case of <, the XML parser is implicitly looking for the tag name and the end tag >. However, in your particular case, you were using < as a JavaScript operator, not as an XML entity. This totally explains the XML parsing error you got:

    The content of elements must consist of well-formed character data or markup.

    In essence, you're writing JavaScript code in the wrong place, a XML document instead of a JS file, so you should be escaping all XML special characters accordingly. The < must be escaped as &lt;.

    So, essentially, the

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

    must become

    for (var i = 0; i &lt; length; i++) {
    

    to make it XML-valid.

    However, this makes the JavaScript code harder to read and maintain. As stated in Mozilla Developer Network's excellent document Writing JavaScript for XHTML, you should be placing the JavaScript code in a character data (CDATA) block. Thus, in JSF terms, that would be:

    <h:outputScript>
        <![CDATA[
            // ...
        ]]>
    </h:outputScript>
    

    The XML parser will interpret the block's contents as "plain vanilla" character data and not as XML and hence interpret the XML special characters "as-is".

    But, much better is to just put the JS code in its own JS file which you include by <script src>, or in JSF terms, the <h:outputScript>.

    <h:outputScript name="functions.js" target="head" />
    

    This way you don't need to worry about XML-special characters in your JS code. Additional advantage is that this gives the browser the opportunity to cache the JS file so that average response size is smaller.

    See also: