Getting jQuery to work with another library (XML parsing) - javascript

We are using jQuery to generate an XML fragment and then convert it to a string using the html() function. But as we just found out, and if anyone doesn't know, the html() JavaScript function as implemented in IE is broken, broken, broken. Basically, it capitalizes some tags, adds attributes to others "helpfully" (in our case, ), and generally doesn't do the Right Thing.
I would like to use something like this to generate the XML string instead:
http://www.stainlessvision.com/jquery-html-vs-innerxhtml
However, this library won't play nicely with jQuery out of the box, e.g.:
var $dummyRoot = $('<dummyroot/>'); // since html() doesn't generate the outer element
var $foo = $('<foo></foo>');
var $font = $('<font ></font >');
$foo.append($font);
$dummyRoot.append($foo);
var $s = innerXHTML($dummyRoot); // <-- Doesn't work
I think it wants a more W3C DOM-ish object.
How can I get jQuery to talk to this innerXHTML() function; or, alternatively, is there another function I can use (maybe something built into jQuery or a jQuery plugin))?
Edit: Follow up for DDaviesBrackett's question. I also have a "body" element in my XML; look how it picks up CSS styling (and not just a element).
Is there an unwritten rule to not generate XML inside the DOM whose elements have names like body, font, head, etc.?
var $dummyRoot = $('<dummyroot/>');
var $foo = $('<foo></foo>');
var $body = $('<body></body>');
var $font = $('<font></font>');
$body.append($font);
$foo.append($body);
$dummyRoot.append($foo);
var $s = innerXHTML($dummyRoot[0]);
// $s equals "<foo><body bottommargin="15" leftmargin="10" rightmargin="10" topmargin="15"><font size="+0"></font></body></foo>"

the jQuery object wraps its contents, but exposes them via an array indexer. What do you get when you use
var $s = innerXHTML($dummyRoot[0]);
instead of your example?

Is there an unwritten rule to not generate XML inside the DOM whose elements have names like body, font, head, etc.?
jQuery relies on the innerHTML property to parse a given piece of text and construct the DOM from that. It was never meant to parse or generate XML as colliding names can give totally unpredictable results depending on how the browser sees it.
See
jQuery won’t parse xml with nodes called option
How do I parse xml with jQuery?
Parse content like XML, with jQuery
I have given a similar answer for generating proper XML in fewer steps using a recursive approach. To create the following XML:
<foo>
<body>
<font></font>
</body>
</foo>
you would write:
Σ('foo',
Σ('body',
Σ('font', '')
)
);
Σ just looks cooler, but you can change the function name to whatever you want :)

Related

Dealing with jQuery parseHTML() and html() when using Google closure compiler,,,

I have a simple set of statements that actually work (I have run that code and it does what I expect of it) that looks as follow:
result_xml = result.jqxhr.responseXML;
a = jQuery("data[name='calendar']", result_xml).text();
new_calendar = jQuery.parseHTML(a);
jQuery("div.calendar").html(jQuery("div.calendar table.calendar-table", new_calendar));
result comes from an AJAX reply, you should recognize the jqxhr field.
I expect a to be a string since I get a block of data with text(). The data is an HTML string that I saved in the XML document. So I can cast it this way:
a = /** #type {string} */ jQuery("data[name='calendar']", result_xml).text();
Now I convert that string to an HTML DOM thinking it would be an Element:
new_calendar = /** #type {Element} */ jQuery.parseHTML(a);
And that's where I get this weird error:
WARNING - invalid cast - must be a subtype or supertype
from: (Array.<(Element|null)>|null)
to : (Element|null)
So parseHTML() would be returning an array of elements (or null)?
Then I was trying to use the output of the parseHTML() in the html() function and there too, I have a hard time to understand what is going on.
WARNING - invalid cast - must be a subtype or supertype
found : (Array.<(Element|null)>|null)
required: (Document|Element|Object.|jQuery|null|undefined)
Frankly, I do not see why the Google compiler makes it such that the output of one function cannot be the input of another, even if it works just fine in the real world.
Would an array of Element be considered a Document?
Then finally (yeah! all of that for 3 lines of JavaScript!) I get another error in regard to the input of the html() function:
WARNING - actual parameter 1 of jQuery.prototype.html does not match formal parameter
found : jQuery
required: (function (number, string): ?|string|undefined)
jQuery("div.calendar").html(jQuery("div.calendar table.calendar-table", new_calendar));
Here too, it works in the real world... will the jQuery object automatically be converted to a string and then re-transformed to a set of tags to be added in my calendar?
Are all of these normal limitations of the closure compiler?
Based on Chad answer, I changed my code this way:
result_xml = result.jqxhr.responseXML;
a = /** #type {string} */ (jQuery("data[name='calendar']", result_xml).text());
new_calendar = jQuery.parseHTML(a);
jQuery("div.calendar").empty().append(jQuery("div.calendar table.calendar-table", new_calendar[0]));
I cast the text() output to string in closure;
I use element 0 of new_calendar instead of directly new_calendar.
I changed the html() with empty().append() since the append() function accepts a jQuery object as input
This makes the closure compiler happy.
Yes Absolutely this is expected behavior. There are many things that "work" in JavaScript. Closure-compiler attempts to force good behavior when asked.
As for jQuery - these are the normal limitations of the published jQuery API. With jQuery the code often supports undocumented behaviors (like you are listing). However the jQuery team is free to change behaviors that are not documented at any time.
Closure-compiler warns you if you are not matching what is listed as the API spec (and that definition is community maintained - so not always 100% right).
Here's the relevant specifications:
jQuery().html(string) - A string of HTML to set as the content of each matched element
jQuery.parseHTML('string') - Returns: Array
I use DOM API instead of jQuery.
There is a full HTML text in data.
// create DOM parser object
var dom_parser = new DOMParser();
// parse HTML into DOM document object
var doc = dom_parser.parseFromString(data , "text/html");
// doc.body.innerHTML = body part in text, not object
// <body> tag is stripped.
// append the body part into div with the id "contentsarea".
$("#contentsarea").empty().append(doc.body.innerHTML);

Can jquery manipulate a temporary document created with DOM?

The thing that I want to achieve is to manipulate a document created with DOM using jquery by passing a big html string. Consider the following example:
var doctype = document.implementation.createDocumentType( 'html', '', '');
var dom = document.implementation.createDocument('', 'html', doctype);
dom.documentElement.innerHTML = '<head><head><title>A title</title></head><body><div id="test">This is another div</div></body>';
This will create a new document in dom, with the content provided. Now I want to use jquery to append let's say a div inside the existing div.
$('#test',dom).append('<div> A second div</div>');
console.log(dom);
When I print the result in the console it seems that the innerHTML of the 'dom' has not changed. From the API documentation of jquery,http://api.jquery.com/jQuery/ more specific "jQuery( selector [, context ] )" function should allow this.
Since someone may argue about using the console to debug, I am providing below another part of code that does not work:
$('body').append($('#test',dom));
Tested in chrome and firefox and it does not work with the latest jquery library.
By changing the constructors and using the line below
var dom = document.implementation.createHTMLDocument("Test");
instead of the two lines originally introduced
var doctype = document.implementation.createDocumentType( 'html', '', '');
var dom = document.implementation.createDocument('', 'html', doctype);
everything works fine, even when setting the innerHTML directly!
It would appear that it is setting the entire HTML content through innerHTML that does not work.
From experimenting with your code, you'll notice that the following doesn't yield any result either:
dom.documentElement.getElementsByTagName('body')
And that dom.body is null. However, if you would construct the objects rather than to just set the innerHTML, both the above and the jQuery selectors will work:
dom.body = document.createElement('body');
dom.body.appendChild(document.createElement('div'));
console.log($('div', dom));
Yes its possible. You have to create secont instance of jQuery. Check this fiddle: http://jsfiddle.net/hDcUp/
var jq2 = jQuery(dom);

Create 2d array from string

I have the following string :
[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],]
How can I create a 2d array of strings from it ?
EDIT
I've removed html tags since they're not the problem here. Also I'd like to do it without using any additional libs to keep it lightweight.
Except from the HTML tags in it, it would be valid JSON. You could remove the HTML tags and parse it using any library that handles JSON, like jQuery:
var arr = $.parseJSON(theString.replace(/<br\/>/g,''));
It would also be valid Javascript code with the HTML tags removed, so if you have full control over where the string comes from so that you are certain that it can never contain any harmful code, you could use the eval function to execute the string:
// Warning: 'eval' is subject to code injection vulnerabilities
var arr = eval(theString.replace(/<br\/>/g,''));
You will need to remove the <br/> from the string. Then you should be able to do:
var my2darray = eval(mystring);

jquery xml parsing question

Say I have xml like this:
<outer>
<inner name="name0" type="type0" />
<inner name="name1" type="type1" />
<inner name="name2" type="type2" />
</outer>
I'm parsing the xml using jquery and I'm inside the each loop of the outer tag:
$(xml).find('outer').each(function() {
//what goes here?
});
Say I know the values that can appear in the name id of <inner>. How do I, in the code above, get the appropriate type from the given <inner> tag.
Example:
I have the string var name = "name1"
Inside the each loop, I need to pull type1 from the <inner> tag that has name="name1". Thanks
I'm parsing the xml using jquery
Not really. $() does not include an XML parser; you are parsing it as HTML, using the browser's innerHTML parser. Because your input is not valid HTML, you might get any old strange DOM as the output, especially in IE, which doesn't like custom elements much.
It's annoyingly tricky to get an XML parser in a cross-browser way; it is much less well-supported than having XMLHttpRequest return an XML document. In many modern browsers you can ask for a new DOMParser, but for IE you have to create an MSXML2.DOMDocument ActiveXObject, and for a few older browser you have to document.implementation.createDocument (and even then the load method isn't standard or supported everywhere).
You can use an attribute-equals selector, like this:
var name="name1";
$(xml).find('outer').each(function() {
var type = $(this).children("[name='" + name + "']").attr("type");
//use type
});
You can give it a try here, if the XML is exactly what you posted and not a subset, just remove the .find('outer'), since the root element is already where you want to be.
var name = "name1";
$(xml).find('outer').each(function() {
var type = $(this).children('[name=' + name + ']').attr('type');
});

Is it possible to get jquery objects from an html string thats not in the DOM?

For example in javascript code running on the page we have something like:
var data = '<html>\n <body>\n I want this text ...\n </body>\n</html>';
I'd like to use and at least know if its possible to get the text in the body of that html string without throwing the whole html string into the DOM and selecting from there.
First, it's a string:
var arbitrary = '<html><body>\nSomething<p>This</p>...</body></html>';
Now jQuery turns it into an unattached DOM fragment, applying its internal .clean() method to strip away things like the extra <html>, <body>, etc.
var $frag = $( arbitrary );
You can manipulate this with jQuery functions, even if it's still a fragment:
alert( $frag.filter('p').get() ); // says "<p>This</p>"
Or of course just get the text content as in your question:
alert( $frag.text() ); // includes "This" in my contrived example
// along with line breaks and other text, etc
You can also later attach the fragment to the DOM:
$('div#something_real').append( $frag );
Where possible, it's often a good strategy to do complicated manipulation on fragments while they're unattached, and then slip them into the "real" page when you're done.
The correct answer to this question, in this exact phrasing, is NO.
If you write something like var a = $("<div>test</div>"), jQuery will add that div to the DOM, and then construct a jQuery object around it.
If you want to do without bothering the DOM, you will have to parse it yourself. Regular expressions are your friend.
It would be easiest, I think, to put that into the DOM and get it from there, then remove it from the DOM again.
Jquery itself is full of tricks like this. It's adding all sorts off stuff into the DOM all the time, including when you build something using $('<p>some html</p>'). So if you went down that road you'd still effectively be placing stuff into the DOM then removing it again, temporarily, except that it'd be Jquery doing it.
John Resig (jQuery author) created a pure JS HTML parser that you might find useful. An example from that page:
var dom = HTMLtoDOM("<p>Data: <input disabled>");
dom.getElementsByTagName("body").length == 1
dom.getElementsByTagName("p").length == 1
Buuuut... This question contains a constraint that I think you need to be more critical of. Rather than working around a hard-coded HTML string in a JS variable, can you not reconsider why it's that way in the first place? WHAT is that hard-coded string used for?
If it's just sitting there in the script, re-write it as a proper object.
If it's the response from an AJAX call, there is a perfectly good jQuery AJAX API already there. (Added: although jQuery just returns it as a string without any ability to parse it, so I guess you're back to square one there.)
Before throwing it in the DOM that is just a plain string.
You can sure use REGEX.

Categories