Interesting feature with parsing XML with jQuery on Safari

I've been playing around with HTML5 quite a bit recently, in particular with offline Web applications.

My second experiment (my first is on pause) was with making my video games available, so that I can access the listing when I'm out shopping at used game stores.

It's still in progress, but you can see my offline listing of video games now.

My main intention is to make this available on my iPod Touch, so I was a bit dismayed when I found that the listing didn't display the title of the game. Everything else displayed just fine, but not the titles. Naturally, after some searching about I posted my question to Stack Overflow - XMLDocument (via jQuery ajax call) stored as string in localStorage results in Safari not finding title elements.

After further research, it looks like I was missing a parseXML call, which results in the data correctly being pulled/displayed in Safari.

I decided to do some further testing on this issue, and I believe I've discovered why it's displaying as it is.

The test code

I created a bit of test code - jQuery XML parsing - and that I've posted below.

<!DOCTYPE html>
<html>
<head>
    <title>jQuery XML Parsing testing - JamesRSkemp.com</title>
</head>
<body>
	<h1>jQuery XML Parsing testing</h1>
	<p>The following test code was created to test Internet Explorer 9, Chrome 10, Firefox 4, Opera 11, Safari 5 (Windows), and Safari on iOS 4.3. Read more in <a href="http://strivinglife.com/words/post/Interesting-feature-with-parsing-XML-with-jQuery-on-Safari.aspx" rel="external">Interesting feature with parsing XML with jQuery on Safari</a>.</p>
	<noscript>You must have JavaScript enabled to view this test.</noscript>
	<div id="TestOutput"></div>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
	<script type="text/javascript">
		// We'll store an example of an XML file as a string of text.
		var xmlContent = "<?xml version='1.0'?><Books><Book><Title>Book 1</Title><Author>Book 1 Author</Author></Book><Book><Title>Book 2</Title><Author>Book 2 Author</Author></Book><Book><Title>Book 3</Title><Author>Book 3 Author</Author></Book><Book><Title>Book 4</Title><Author>Book 4 Author</Author></Book><Book><Title>Book 5</Title><Author>Book 5 Author</Author></Book></Books>";
		var testOutputText = "";
	var simpleCall = $(xmlContent);
	var simpleParse = $.parseXML(xmlContent);
	var parse = $($.parseXML(xmlContent));

	try {
		testOutputText += "Type of $(xmlContent): " + typeof simpleCall + "&lt;br /&gt;";
		testOutputText += "To string: " + simpleCall.toString() + "&lt;br /&gt;";
		testOutputText += "Length: " + simpleCall.length + "&lt;br /&gt;";
		if (typeof simpleCall === 'object') {
			for (var prop in simpleCall) {
				//testOutputText += "	property: " + prop + " value: [" + simpleCall[prop] + "]\n";
			}
		}
		simpleCall.find("Book").each(function () {
			testOutputText += "&lt;em&gt;" + $(this).find('Title').text() + "&lt;/em&gt; by " + $(this).find("Author").text() + "&lt;br /&gt;";
		});
	} catch (e) {
		testOutputText += "&lt;span style='color:red;'&gt;There was an error processing at this point.&lt;/span&gt;&lt;br /&gt;";
	}

	try {
		testOutputText += "Type of $.parseXML(xmlContent): " + typeof simpleParse + "&lt;br /&gt;";
		testOutputText += "To string: " + simpleParse.toString() + "&lt;br /&gt;";
		testOutputText += "Length: " + simpleParse.length + "&lt;br /&gt;";
		if (typeof simpleParse === 'object') {
			for (var prop in simpleParse) {
				//testOutputText += "	property: " + prop + " value: [" + simpleParse[prop] + "]\n";
			}
		}
		simpleParse.find("Book").each(function () {
			testOutputText += "&lt;em&gt;" + $(this).find('Title').text() + "&lt;/em&gt; by " + $(this).find("Author").text() + "&lt;br /&gt;";
		});
	} catch (e) {
		testOutputText += "&lt;span style='color:red;'&gt;There was an error processing at this point.&lt;/span&gt;&lt;br /&gt;";
	}

	try {
		testOutputText += "Type of $($.parseXML(xmlContent)): " + typeof parse + "&lt;br /&gt;";
		testOutputText += "To string: " + parse.toString() + "&lt;br /&gt;";
		testOutputText += "Length: " + parse.length + "&lt;br /&gt;";
		if (typeof parse === 'object') {
			for (var prop in parse) {
				//testOutputText += "	property: " + prop + " value: [" + simpleParse[prop] + "]\n";
			}
		}
		parse.find("Book").each(function () {
			testOutputText += "&lt;em&gt;" + $(this).find('Title').text() + "&lt;/em&gt; by " + $(this).find("Author").text() + "&lt;br /&gt;";
		});
	} catch (e) {
		testOutputText += "&lt;span style='color:red;'&gt;There was an error processing at this point.&lt;/span&gt;&lt;br /&gt;";
	}

	$('#TestOutput').append(testOutputText);
&lt;/script&gt;

</body> </html>

What the test does

I did a couple of things with this test.

First, I created a string with the following XML, and saved it as xmlContent.

<?xml version='1.0'?>
<Books>
	<Book>
		<Title>Book 1</Title>
		<Author>Book 1 Author</Author>
	</Book>
	<Book>
		<Title>Book 2</Title>
		<Author>Book 2 Author</Author>
	</Book>
	<Book>
		<Title>Book 3</Title>
		<Author>Book 3 Author</Author>
	</Book>
	<Book>
		<Title>Book 4</Title>
		<Author>Book 4 Author</Author>
	</Book>
	<Book>
		<Title>Book 5</Title>
		<Author>Book 5 Author</Author>
	</Book>
</Books>

Next I created three variables, storing different things. First, I did $(xmlContent), then $.parseXML(xmlContent), and finally $($.parseXML(xmlContent)).

The results

Interestingly, on IE 9, Chrome 10, Firefox 4, Opera 11, Safari 5 (Windows), the first and third tests return the type of object Object.

For the second test we have object Document on IE 9, Chrome 10, Safari 5, and Safari on iOS 4.3, and object XMLDocument on Firefox 4 and Opera 11.

But for the first test, while we have a length of 2 on IE 9, Chrome 10, Firefox 4, and Opera 11, we only have a length of 1 on Safari 5 and Safari on iOS 4.3.

The fix, then, is to make sure to use the last of the three, but it seems odd that this wouldn't work on only one of the tested browsers.