views:

61

answers:

2

I need to serve XHTML pages that include Javascript. My problem is that Firefox (3.5.7) seems to ignore the Javascript. For example:

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
  <head>
    <title>My Title</title>
  </head>
  <body>
    <script type="text/javascript">
      document.write("Hello world!");
    </script>
  </body>
</html>

If I save this as test.html, Firefox displays this correctly. If I save this as test.xml, Firefox displays a blank page. What am I doing wrong here?

+7  A: 

From http://www.w3.org/MarkUp/2004/xhtml-faq#docwrite

Does document.write work in XHTML?

No. Because of the way XML is defined, it is not possible to do tricks like this, where markup is generated by scripting while the parser is still parsing the markup.

You can still achieve the same effects, but you have to do it by using the DOM to add and delete elements.

meouw
OMG. Does this mean that many (most?) Javascript libraries out there will simply not work?
lindelof
I'd hope that most JavaScript libraries wouldn't use document.write. It's nasty.
David Dorward
Important second part to this answer: **That doesn't mean you can't use JavaScript with XHTML** It just means you can't use `document.write`. Instead, as the W3C quote says, once the page is loaded you can use DOM manipulation to add/modify content. It's a bit of a pain writing JavaScript within the XHTML file itself; you're best of locating it in a separate JavaScript file and using the `src` attribute of the `script` tag to bring it in.
T.J. Crowder
+2  A: 

document.write does not work but setting innerHTML is still working. Just remember that whatever you set in there must be well-formed.

This means that you can use jQuery to add new element/set html to any element.

I have a small library used for this. It work quite a bit.


function IsXHTML() {
    if (document.doctype == null)
        return false;

    var vPublic = document.doctype.publicId.replace(/^.*(.html).*$/i, '$1').toLowerCase();
    var vSystem = document.doctype.systemId.replace(/^.*(.html).*$/i, '$1').toLowerCase();
    return (vPublic == 'xhtml')
        && (vSystem == 'xhtml');
};

function XHTMLDocWrite($Str, $Element) {
    if ($Str == null)
        return;

    var vIsMozilla = !window.opera && !/Apple/.test(navigator.vendor);
    var vIsOpera   =  window.opera;

    // Make sure &s are formatted properly
    if (!vIsOpera)
         $Str = $Str.replace(/&(?![#a-z0-9]+;)/g, "&amp;");
    else $Str = $Str.replace(/&(?![#a-z0-9]+;)/g, "&");

    // - Mozilla assumes that everything in XHTML, innerHTML is actually XHTML
    // - Opera and Safari assume that it's XML
    if ( !vIsMozilla )
        $Str = $Str.replace(/(<[a-z]+)/g, "$1 xmlns='http://www.w3.org/1999/xhtml'");

    // The HTML needs to be within a XHTML element
    var vDiv = document.createElementNS("http://www.w3.org/1999/xhtml","div");
    vDiv.innerHTML = $Str;

    if ($Element == null) {
        // Find the last element in the document
        var vLastElmt = document.getElementsByTagName("*");
        vLastElmt = vLastElmt[vLastElmt.length - 1];
        $Element = vLastElmt;
    }

    // Add all the nodes in that position
    var vNodes = vDiv.childNodes;
    while (vNodes.length)
        $Element.parentNode.appendChild( vNodes[0] );
};

function UseXHTMLDocWrite($IsForced) {
    if (!IsXHTML() && !$IsForced)
        return;
    if (document.write == XHTMLDocWrite)
        return;

    document.write = XHTMLDocWrite;
};

You run UseXHTMLDocWrite so that document.write will be replaced with XHTMLDocWrite if needed.

This will simply add content to the last added element. However, it does not work with nested element.

NOTE: I modify the code of XHTMLDocWrite from the code I got from some where on the Internet and cannot seems to find it anymore. So sorry I can't credit him.

Hope this helps.

NawaMan
AWESOME. But remember to wrap your script in CDATA if you include it as-is on your page, escpecially if it's for XHTML.
lindelof