<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>This Much I Know &#187; Tim Down</title>
	<atom:link href="http://www.thismuchiknow.co.uk/?author=2&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.thismuchiknow.co.uk</link>
	<description>(hopefully other people will find it useful too)</description>
	<lastBuildDate>Tue, 15 Jun 2010 08:31:04 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Empty XHTML tags and Internet Explorer DOM traversal</title>
		<link>http://www.thismuchiknow.co.uk/?p=184</link>
		<comments>http://www.thismuchiknow.co.uk/?p=184#comments</comments>
		<pubDate>Thu, 15 Oct 2009 15:11:59 +0000</pubDate>
		<dc:creator>Tim Down</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://www.thismuchiknow.co.uk/?p=184</guid>
		<description><![CDATA[Internet Explorer behaviour with HTML and XHTML pages containing empty elements with no end tag such as <code>&#60;span /&#62;</code>.]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s the problem: HTML and XHTML pages containing empty elements with no end tag such as <code>&lt;span /&gt;</code> break JavaScript DOM traversal methods in Internet Explorer 6, 7 and 8, resulting in nodes after such an element showing up in more than one node&#8217;s <code>childNodes</code> collection.<span id="more-184"></span></p>
<p>Consider the following XHTML document:</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&gt;
&lt;head&gt;
    &lt;title&gt;Test&lt;/title&gt;
    &lt;script type="text/javascript"&gt;
        function show() {
            var span = document.getElementById("span");
            alert(span.innerHTML);
        }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body onload="show();"&gt;
&lt;p id="p1"&gt;Paragraph containing some text followed by an empty span&lt;span id="span"/&gt;&lt;/p&gt;
&lt;p id="p2"&gt;Second paragraph just containing text&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>The idea is that when the page loads, the JavaScript will get a reference to the empty span and display its HTML contents. That will be an empty string, right? Not in IE it won&#8217;t. In IE, you get the following:</p>
<p><code>&lt;/P&gt;<br />
&lt;P id=p2&gt;Second paragraph just containing text&lt;/P&gt;</code></p>
<p>Now, while <code>&lt;span /&gt;</code> may be valid in documents with an XHTML Strict doctype, this makes not a jot of difference to IE, which always parses XHTML as HTML, regardless of doctype. What seems to happen is that IE finds the offending span tag (henceforth known as &#8216;Bad Span&#8217;) and ignores the closing slash. Whether it does this because it knows that such a tag is invalid in HTML or because it always ignores closing slashes I&#8217;m not sure. Whatever, the result is that IE scans for a corresponding <code>&lt;/span&gt;</code> and since none is forthcoming, skips over the <code>&lt;/p&gt;</code> (which it presumably also considers invalid without a matching <code>&lt;p&gt;</code> inside the span) and carries on to the end of the document, adding subsequent nodes to the <code>&lt;span /&gt;</code>&#8217;s <code>childNodes</code> collection as it goes.</p>
<p>This is not good news. Consider the second <code>&lt;p&gt;</code> element. IE somewhat contrarily knows enough about the proper structure of the document to place it in the childNodes collection of the body element, but as shown above, it also shows up in the Bad Span&#8217;s childNodes, <em>meaning a node can effectively have multiple parents</em>. The DOM is no longer a hierarchy but a map. You may be curious to know which of its parents the second paragraph considers its real parent. The answer (from its <code>parentNode</code> property) is Bad Span.</p>
<p>Now, an unsuspecting piece of JavaScript could easily get into problems. Imagine you had a script that acted on every span in the page. Let&#8217;s say for the sake of an example you wanted to set every span&#8217;s contents to just be the text &#8220;[SPAN]&#8220;. You might do something like the following:</p>
<pre><code>function changeSpans(node) {
    if (node.nodeType == 1 &#038;&#038; node.nodeName == "SPAN") {
        node.innerHTML = "[SPAN]";
    } else {
        // Traverse the node's children to find more links
        for (var i = 0; i < node.childNodes.length; i++) {
            changeSpans(node.childNodes[i]);
        }
    }
}

changeSpans(document.body);</code></code></pre>
<p>Run this in every other browser and you get what you expect, but run this in IE and your second paragraph (in fact, anything after Bad Span) gets wiped out. This is a contrived example, but real life scripts recursively traversing and acting on the DOM could easily fall foul of this problem in IE. At best, you could end up traversing some nodes multiple times, which could be significant if your code is not expecting it.</p>
<p>It&#8217;s not easy to work around, either. I have found no simple way to detect empty elements like Bad Span, since the closing slash is not present in the the span&#8217;s parent element&#8217;s <code>innerHTML</code> property or its own <code>outerHTML</code> property. The only strategies I can see are:</p>
<ul>
<li>Traverse the document from start to finish, keeping a collection of the first parent node you see for each node and ignoring any node whose <code>childNodes</code> collection contains a node you&#8217;ve already seen a different parent for, or:</li>
<li>Icky regular expression-based nastiness to check if each node could possibly have the children it claims to have based on its parent&#8217;s <code>innerHTML</code>.</li>
<p>Neither is very appealing. Suggestions welcome.
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.thismuchiknow.co.uk/?feed=rss2&amp;p=184</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Range.intersectsNode, Mozilla and WebKit</title>
		<link>http://www.thismuchiknow.co.uk/?p=64</link>
		<comments>http://www.thismuchiknow.co.uk/?p=64#comments</comments>
		<pubDate>Tue, 14 Oct 2008 11:43:57 +0000</pubDate>
		<dc:creator>Tim Down</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://www.thismuchiknow.co.uk/?p=64</guid>
		<description><![CDATA[Today I needed to know whether a Range object (obtained from a selection) intersected with a particular node within an HTML document, which is a relatively common scenario when developing WYSWIYG editors. Mozilla&#8217;s implementation of Range used to have a proprietary intersectsNode method, which was ideal. However, they&#8217;ve removed this in Gecko 1.9 (and therefore [...]]]></description>
			<content:encoded><![CDATA[<p>Today I needed to know whether a Range object (obtained from a selection) intersected with a particular node within an HTML document, which is a relatively common scenario when developing WYSWIYG editors. Mozilla&#8217;s implementation of Range used to have a proprietary <code>intersectsNode</code> method, which was ideal. However, they&#8217;ve <a href="http://developer.mozilla.org/en/Gecko_1.9_Changes_affecting_websites#intersectsNode_has_been_removed">removed this in Gecko 1.9</a> (and therefore Firefox 3) and <a href="http://developer.mozilla.org/en/DOM/range.intersectsNode">provided an example function that does the same job</a>. Unfortunately this function doesn&#8217;t work in WebKit (and hence Safari and Chrome) because of <a href="https://bugs.webkit.org/show_bug.cgi?id=20738">a bug in WebKit</a>: they seem to have managed to invert the behaviour of <code>compareBoundaryPoints</code> with <code>END_TO_START</code> and <code>START_TO_END</code>. Tsk. So I&#8217;ve modified the Mozilla example to work consistently with all browsers that support <code>Range</code><span id="more-64"></span>:</p>
<pre style="padding: 20px 5px"><code>function rangeIntersectsNode(range, node) {
    var nodeRange = node.ownerDocument.createRange();
    try {
        nodeRange.selectNode(node);
    } catch (e) {
        nodeRange.selectNodeContents(node);
    }

    var rangeStartRange = range.cloneRange();
    rangeStartRange.collapse(true);

    var rangeEndRange = range.cloneRange();
    rangeEndRange.collapse(false);

    var nodeStartRange = nodeRange.cloneRange();
    nodeStartRange.collapse(true);

    var nodeEndRange = nodeRange.cloneRange();
    nodeEndRange.collapse(false);

    return rangeStartRange.compareBoundaryPoints(
             Range.START_TO_START, nodeEndRange) == -1 &#038;&#038;
           rangeEndRange.compareBoundaryPoints(
             Range.START_TO_START, nodeStartRange) == 1;
}</code></pre>
<p>Note: WebKit does implement the <code>intersectsNode</code> method on <code>Range</code>. However, I prefer not to use it in the above function because it&#8217;s not specified in the <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html">DOM standard for Range</a> and may therefore end up being removed from WebKit, and there&#8217;s more chance of the function behaving consistently between browsers if it uses the same code in each.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thismuchiknow.co.uk/?feed=rss2&amp;p=64</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript eval in iframes</title>
		<link>http://www.thismuchiknow.co.uk/?p=25</link>
		<comments>http://www.thismuchiknow.co.uk/?p=25#comments</comments>
		<pubDate>Mon, 22 Jan 2007 14:50:14 +0000</pubDate>
		<dc:creator>Tim Down</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://www.thismuchiknow.co.uk/?p=25</guid>
		<description><![CDATA[In the course of developing a new version of log4javascript, I was adding a command line feature that required the ability to evaluate and execute code in an iframe from within the main window. Fine, I thought, I&#8217;ll just call eval on the iframe window object. Which predictably worked fine in Firefox and Opera 8+, [...]]]></description>
			<content:encoded><![CDATA[<p>In the course of developing a new version of <a target="_blank" title="log4javascript (opens in new window)" href="http://www.timdown.co.uk/log4javascript">log4javascript</a>, I was adding a command line feature that required the ability to evaluate and execute code in an iframe from within the main window. Fine, I thought, I&#8217;ll just call <code>eval</code> on the iframe window object. Which predictably worked fine in Firefox and Opera 8+, but did nothing in IE, which seemed to be missing the eval method on the iframe.</p>
<p><span id="more-25"></span></p>
<p>After some googling I came across a mention of the IE-specific execScript method of window objects, which looked like it might do the job. However, I got my implementation slightly wrong but then noticed that after the first (wrong) call to execScript my original code was working! It turns out that calling execScript once on a window object causes the eval method magically to appear. This is true of every version of IE I&#8217;ve tested in Windows (7, 6, 5.5, 5, even 4); I haven&#8217;t tried IE on the Mac, or Safari, yet.</p>
<p>So I have a nice easy workaround in IE that still works in other browsers:</p>
<p><code><br />
function evalIframe(iframe, command) {<br />
&nbsp;&nbsp;if (!iframe.eval &#038;&#038; iframe.execScript) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;iframe.execScript("null");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;iframe.eval(command);<br />
}</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.thismuchiknow.co.uk/?feed=rss2&amp;p=25</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
