does innerHTML work with XML Elements? - javascript

According to JavaScript: The Definitive Guide: "Despite its name, innerHTML can be used with XML elements as well as HTML elements".
However, when I actually attempt to access the innerHTML property of an XML Element object, undefined is returned:
var xml = $.ajax({url: "test.xml", async: false}).responseXML.documentElement;
console.log(xml.innerHTML); // displays "undefined" in console log
What is the explanation for this behavior?
test.xml:
<?xml version="1.0" encoding="utf-8"?>
<foo><bar>baz</bar></foo>

The earlier answers don't address the newer browsers that exist as of September 2014. When an XML document is created with:
var parser = new DOMParser();
var doc = parser.parseFromString(data, "text/xml");
where data is the XML document as a string, then the following are true:
In Firefox 28, and Chrome 36, the nodes in doc have an innerHTML field that contains the XML serialization of the contents of the node.
In IE 9, 10 and 11, Safari 7.0 and Opera 12, the nodes in doc have neither an innerHTML field nor an xml field. I was not able to identify something that could stand for these fields. There does not appear to be any other option than using XMLSerializer for these browsers.

The explanation is that the book is wrong on this point.
XML elements in IE have a non-standard xml property that provides the equivalent of innerHTML. For other browsers you'll need to use an XMLSerializer.

HTML5 defines there to be innerHTML on XML Elements (despite the name, using XML parser/serializer). Nothing implements this yet. Opera has for a while supported innerHTML in XHTML (though not XML generally), but uses the HTML parser/serializer for it.

You can do so, but I'd rather prefer to use jQuery because it is easier to implement. The jQuery way is selecting the element container that you want. Then you can use the html() property to change or get the innerHTML of your element.
$("myxmltag").html("<achildtag>Hello</achildtag><anotherchildtag>World!</anotherchildtag>");
For more information about this property, refer to: http://api.jquery.com/html/
Also, don't forget the differences between html() and text(), and you can use other properties to manipulate the elements within a node in your xml using jQuery, like append(), prepend(), after(), etc...

Related

How to apply styles for appended element? [duplicate]

I have a little testcase over at:
http://jsfiddle.net/9xwUx/1/
The code boils down to the following (given a node with id "target"):
var string = '<div class="makeitpink">this should be pink, but is not</div>';
var parser = new DOMParser();
var domNode = parser.parseFromString(string,"text/xml");
document.getElementById("target").appendChild(domNode.firstChild);
If you run the testcase, and then inspect the target node via firebug/chrome web inspector and select any node within the body tag of jsfiddle's iframe, and do "edit as HTML", add a random charachter anywhere as a string [not an attribute to a domnode, to be clear], and "save", the style is applied. but not before that.
To say that i'm confused is an understatement.
Can anybody please clarify what is going on here?
Thanks.
You can change the mime type to text/html and do the following:
var parser = new DOMParser()
var doc = parser.parseFromString(markup, 'text/html')
return doc.body.firstChild
I didn't test on every browser but it works on Chrome and Firefox. I don't see any reason it wouldn't work elsewhere.
a bit late, but the reason is that you have parsed these using the text/xml option, which means that the results are XML nodes, which don't have CSS applied to them. When you right-click and go "edit as HTML" the browser reinterprets them as HTML and the change in the element will cause a redraw, reapplying the CSS.
I've been parsing my using the relatively hack-ish, yet definitely working method of creating a temporary element and manipulating the innerHTML property, making the browser do the parsing instead:
var temp = document.createElement("div")
//assuming you have some HTML partial called 'fragment'
temp.innerHTML = fragment
return temp.firstChild
Which you've noted in your jsfiddle. Basically it boils down to the output of the DOMParser being an instance of XMLDocument when you use the text/xml option.

Can't create XML node with cyrillic name in IE11

I need to create a xml document (with JavaScript) containing nodes, which is named in russian.
I get InvalidCharacterError in IE11 when trying run doc.createElement("Выборка")
doc is created with var doc = document.implementation.createDocument("", "", null)
In other browsers this code is working without any issues.
How can be solved? What is the root of an issue?
jsFiddle example: http://jsfiddle.net/e4tUH/1/
My post on connect.microsoft.com: https://connect.microsoft.com/IE/feedback/details/812130/cant-create-xml-node-with-cyrillic-name-in-ie11
Current workaround: Switch IE11 to IE10 with X-UA-Compatible meta-tag and use window.ActiveXObject(...) to create XML documents.
Maybe IE11 has an issue similar to what Firefox had in the past:
https://bugzilla.mozilla.org/show_bug.cgi?id=431701
That means that although your page is loading the correct encoding, IE11 is creating the new document with a default encoding which is not the expected one. There's no way to check that besides looking into IE11 source code, which we don't have.
Have you trying to add non-ASCII characters in other places besides element names? Like an attribute value or a text node?
I searched how to change the created document encoding and haven't found any solution for that.
To solve your problem I would suggest to use a DOMParser and generate a document from a XML string, like the following:
var parser=new DOMParser();
var xmlDoc=parser.parseFromString('<?xml version="1.0" encoding="UTF-8"?><Выборка>Выборка текста</Выборка>',"text/xml");
All browsers seems to support it for XML parsing. More about DOMParser on the following links, including how to provide backward compatibility with older IE versions:
http://www.w3schools.com/dom/dom_parser.asp
https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
If you don't want to generate your XML just by concatenating strings, you can use some kind of XML builder like in this example: http://jsfiddle.net/UGYWx/6/
Then you can easily create your XML in a more safe manner:
var builder = new XMLBuilder("rootElement");
builder.text('Some text');
var element = builder.element("someElement", {'attr':'value'});
element.text("This is a text.");
builder.text('Some more Text');
builder.element("emptyElement");
builder.text('Even some more text');
builder.element("emptyWithAttributes", {'a1': 'val1', 'a2' : 'val2'});
$('div').text(builder.toString());
I have always been very reluctant to use non-ASCII characters inside source code. Try escaping the string; maybe it helps.
doc.createElement("\u0412\u044B\u0431\u043E\u0440\u043A\u0430")

Custom self-closing / unpaired tags in HTML?

The following code [jsfiddle]...
var div = document.createElement("div");
div.innerHTML = "<foo>This is a <bar /> test. <br> Another test.</foo>";
alert(div.innerHTML);
...shows this parsed structure:
<foo>This is a <bar> test. <br> Another test.</bar></foo>
i.e. the browser knows that <br> has no closing tag but since <bar> is an unknown tag to the browser, it assumes that it needs an closing tag.
I know that the /> (solidus) syntax is ignored in HTML5 and invalid in HTML4, but anyway would like to teach somehow the browser that <bar> does not need an ending tag and I can omit it. Is that possible?
Yes, I'm trying to (temporarily) misuse the HTML code for custom tags and I have my specific reasons to do that. After all, browsers should ignore unknown tags and treat them just like unstyled inline tags, so I should not break anything as long I can make sure the tag names won't ever be used in real HTML standards.
You'd have to use Object.defineProperty on HTMLElement.prototype to override the innerHTML setter and getter with your own innerHTML implementation that treats the elements you want as void. Look here for how innerHTML and the HTML parser is implemented by default.
Note though that Firefox sucks at inheritance when it comes to defining stuff on HTMLElement.prototype where it filters down to HTMLDivElement for example. Things should work fine in Opera though.
In other words, what elements are void depends on the HTML parser. The parser follows this list and innerHTML uses the same rules mostly.
So, in other words, unless you want to create your own innerHTML implementation in JS, you probably should just forget about this.
You can use the live DOM viewer though to show others how certain markup is parsed. You'll then probably notice that same end tags will implicitly close the open element.
I have some outdated innerHTML getter (not setter though) code here that uses a void element list. That may give you some ideas. But, writing a setter implementation might be more difficult.
On the other hand, if you use createElement() and appendChild() etc. instead of innerHTML, you shouldn't have to worry about this and the native innerHTML getter will output the unknown elements with end tags.
Note though, you can treat the unknown element as xml and use XMLSerializer() and DOMParser() to do things:
var x = document.createElement("test");
var serializer = new XMLSerializer();
alert(serializer.serializeToString(x));
var parser = new DOMParser();
var doc = parser.parseFromString("<test/>", "application/xml");
var div = document.createElement("div");
div.appendChild(document.importNode(doc.documentElement, true));
alert(serializer.serializeToString(div));
It's not exactly what you want, but something you can play with. (Test that in Opera instead of Firefox to see the difference with xmlns attributes. Also note that Chrome doesn't do like Opera and Firefox.)

using document.createDocumentFragment() and innerHTML to manipulate a DOM

I'm creating a document fragment as follow:
var aWholeHTMLDocument = '<!doctype html> <html><head></head><body><h1>hello world</h1></body></html>';
var frag = document.createDocumentFragment();
frag.innerHTML = aWholeHTMLDocument;
The variable aWholeHTMLDocument contains a long string that is the entire html document of a page, and I want to insert it inside my fragment in order to generate and manipulate the DOM dynamically.
My question is, once I have added that string to frag.innerHTML, shouldn't it load this string and convert it to a DOM object?
After setting innerHTML, shouldn't I have access to the DOM through a property?
I tried frag.childNodes but it doesn't seem to contain anything, and all I want is to just access that newly created DOM.
While DocumentFragment does not support innerHTML, <template> does.
The content property of a <template> element is a DocumentFragment so it behaves the same way. For example, you can do:
var tpl = document.createElement('template');
tpl.innerHTML = '<tr><td>Hello</td><td>world</td></tr>';
document.querySelector('table').appendChild(tpl.content);
The above example is important because you could not do this with innerHTML and e.g. a <div>, because a <div> does not allow <tr> elements as children.
NOTE: A DocumentFragment will still strip the <head> and <body> tags, so it won't do what you want either. You really need to create a whole new Document.
You can't set the innerHTML of a document fragment like you would do with a normal node, that's the problem. Adding a standard div and setting the innerHTML of that is the common solution.
DocumentFragment inherits from Node, but not from Element that contains the .innerHTML property.
In your case I would use the <template> tag. In inherits from Element and it has a nifty HTMLTemplateElement.content property that gives you a DocumentFragment.
Here's a simple helpermethod you could use:
export default function StringToFragment(string) {
var renderer = document.createElement('template');
renderer.innerHTML = string;
return renderer.content;
}
I know this question is old, but I ran into the same issue while playing with a document fragment because I didn't realize that I had to append a div to it and use the div's innerHTML to load strings of HTML in and get DOM Elements from it. I've got other answers on how to do this sort of thing, better suited for whole documents.
In firefox (23.0.1) it appears that setting the innerHTML property of the document fragment doesn't automatically generate the elements. It is only after appending the fragment to the document that the elements are created.
To create a whole document use the document.implementation methods if they're supported. I've had success doing this on Firefox, I haven't really tested it out on other browsers though. You can look at HTMLParser.js in the AtropaToolbox for an example of using document.implementation methods. I've used this bit of script to XMLHttpRequest pages and manipulate them or extract data from them. Scripts in the page are not executed though, which is what I wanted though it may not be what you want. The reason I went with this rather verbose method instead of trying to use the parsing available from the XMLHttpRequest object directly was that I ran into quite a bit of trouble with parsing errors at the time and I wanted to specify that the doc should be parsed as HTML 4 Transitional because it seems to take all kinds of slop and produce a DOM.
There is also a DOMParser available which may be easier for you to use. There is an implementation by Eli Grey on the page at MDN for browsers that don't have the DOMParser but do support document.implementation.createHTMLDocument. The specs for DOMParser specify that scripts in the page are not executed and the contents of noscript tags be rendered.
If you really need scripts enabled in the page you could create an iFrame with 0 height, 0 width, no borders, etc. It would still be in the page but you could hide it pretty well.
There's also the option of using window.open() with document.write, DOM methods or whatever you like. Some browsers even let you do data URI's now.
var x = window.open( 'data:text/html;base64,' + btoa('<h1>hi</h1>') );
// wait for the document to load. It only takes a few milliseconds
// but we'll wait for 5 seconds so you can watch the child window
// change.
setTimeout(function () {
console.log(x.document.documentElement.outerHTML);
x.console.log('this is the console in the child window');
x.document.body.innerHTML = 'oh wow';
}, 5000);
So, you do have a few options for creating whole documents offscreen/hidden and manipulating them, all of which support loading the document from strings.
There's also phantomjs, an awesome project producing a headless scriptable web browser based on webkit. You'll have access to the local filesystem and be able to do pretty much whatever you want. I don't really know what you're trying to accomplish with your full page scripting and manipulation.
For a Firefox add-on, it probably makes more sense to use the document.implementation.createHTMLDocument method, and then go from the DOM that gives you.
With a document fragment you would append elements that you had created with document.createElement('yourElement'). aWholeHTMLDocument is merely text. Also, unless your using frames I'm not sure why you would need to create the whole HTML document just use what is inside the <body> tags.
Use appendChild
see https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment
var fragment = document.createDocumentFragment();
... fragment.appendChild(some element);
document.querySelector('blah').appendChild(fragment);
Here is a solution for converting a HTML string into a DOM object:
let markup = '<!doctype html><html><head></head><body><h1>hello world</h1></body></html>';
let range = document.createRange();
let fragment = range.createContextualFragment(markup); //Creates a DOM object
The string does not need to be a complete HTML document.
Use querySelector() to get a child of the document fragment (you probably want the body, or some child of the body). Then get the innerHTML.
document.body.innerHTML = aWholeHTMLDocument.querySelector("body").innerHTML
or
aWholeHTMLDocument.querySelector("body").childNodes;
See https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment.querySelector

XUL button doesn't appear

I'm one of the developers of TryAgain, a Firefox add-on, that displays a custom error page when a website fails to load. It essentially replaces Firefox's netError.xhtml with a customized version.
However, I've run in to some fairly terminal compatibility problems between 3.0.*-3.6.* and Fx4b5. (An entry in netError.dtd has been renamed, causing a XML parse error in either one version or the other.)
To fix this, I've decided to have the extension dynamically modify the page, opposed to replacing it completely. One of the elements I need to add to netError.xhtml in Fx3 is a <xul:button>. However, by adding it with the following code, nothing appears on the screen:
var div = document.getElementById("errorContent");
var btn = document.createElement("xul:button");
btn.setAttribute("label", "Hello world");
btn.setAttribute("oncommand", "alert('Hello world!');");
div.appendChild(btn);
I see that on the Mozilla Developer Center that there is this note:
Gecko implementation of createElement doesn't conform to the DOM spec for XUL and XHTML documents: localName and namespaceURI are not set to null on the created element. See bug 280692 for details.
What does this entail, and how can I resolve it?
Furthermore, how can I execute the oncommand event through JavaScript?
document.createElement() doesn't accept qualified names. The "xul:button" string you pass makes it create an element called "xul:button" (== its localName), not a XUL "button" element.
On the other hand when parsing XML, <xul:button> is parsed as a qualified name: the parser searches for namespace corresponding to the xul prefix (from a xmlns:xul="" definition in one of the parent elements) and creates the "button" element in the namespace it found.
The note about not conforming to the DOM spec for XUL and (X)HTML means that you can use regular document.createElement("buttton") to create elements in the XUL or HTML namespace in a XUL or HTML document, correspondingly.
Or you could go the hard way and use:
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
document.createElementNS(XUL_NS, "button")
or even with the qualified name, not that there's any reason to do that:
document.createElementNS(XUL_NS, "xul:button")

Categories