views:

177

answers:

5

Hi!

I am actually making a Sidebar Gadget, (which is AJAX-based) and I am looking for a way to extract a single element from an AJAX Request.

The only way I found yet was to do something like that:

var temp = document.createElement("div");
temp.innerHTML = HttpRequest.innerText;
document.body.appendChild(temp);
temp.innerHTML = document.getElementByID("WantedElement").innerText;

But it is pretty ugly, I would like to extract WantedElement directly from the request without adding it to the actual document...

Thank you!

A: 

One option would be to use regular expressions:

var str = response.match(/<div id="WantedElement">(.+)<\/div>/);
str[0]; // contents of div

However, if your server response is more complex, I'd suggest you to use a data format like JSON for the response. Then it would be much cleaner to parse at the client side.

moff
A: 

It's somewhat unusual to pass HTML through an AJAX request; normally you pass a JSON string that the client can evaluate directly, and work with that

That being said, I don't think there's a way to parse HTML in javascript the way you want that's cross-browser, but here's a way to do it in Mozilla derivatives:

var r = document.createRange();
r.selectNode(document.body);
var domNode = r.createContextualFragment(HTTPRequest.innerText);
Michael Mrozek
@Michael: Unfortunately, Windows Desktop Gadgets run in little Internet Explorer "Server" windows, so your Mozilla solution isn't much help here :-)
Andy E
A: 

You could append the response from XMLHttpRequest inside a hidden div, and then call getElementById to get the desired element. Later remove the div when done with it. Or maybe create a function that handles this for you.

function addNinjaNodeToDOM(html) {
    var ninjaDiv = document.createElement("div");
    ninjaDiv.innerHTML = html;
    ninjaDiv.style.display = 'none';
    return ninjaDiv;
}

var wrapper = addNinjaNodeToDOM(HttpRequest.innerText);
var requiredNode = wrapper.getElementById("WantedElement");
// do something with requiredNode
document.body.removeChild(wrapper); // remove when done

The only reason for appending it to the DOM was because getElementById will not work unless its part of the DOM tree. See MDC.

However, you can still run selector and XPath queries on detached DOM nodes. That would save you from having you to append elements to the DOM.

var superNinjaDiv = document.createElement('div');
superNinjaDiv.innerHTML = html;
var requiedNode = superNinjaDiv.querySelector("[id=someId]");
Anurag
+1  A: 

If you're in control of the data, the way you're doing it is probably the best method. Other answers here have their benefits but also they're all rather flawed. For instance, the querySelector() method is only available to Windows Desktop Gadgets running in IE8 mode on the host machine. Regular expressions are particularly unreliable for parsing HTML and should not be used.

If you're not in control of the data or if the data is not transferred over a secure protocol, you should be more concerned about security than code aesthetics -- you may be introducing potential security risks to the gadget and the host machine by inserting unsanitized HTML into the document. Since gadgets run with user or admin level privileges, the obvious security risk is untrusted source/MITM script injection, leaving a hole for malicious scripts to wreak havoc on the machine it's running on.

One potential solution is to use the htmlfile ActiveXObject:

function getElementFromResponse(divId)
{
    var h = new ActiveXObject("htmlfile");
    h.open();

    // disable activex controls
    h.parentWindow.ActiveXObject = function () {};

    // write the html to the document
    h.write(html);

    h.close();
    return h.getElementById("divID").innerText;
}

You could also make use of IE8's toStaticHTML() method, but your gadget would need to be running in IE8 mode.

Andy E
Thank you!I always forget I don't have to be Cross-Browser in gadgets so i can use ActiveX.
m6a-uds
@m6a-uds: It's probably the biggest benefit of developing gadgets and the biggest suitable use for proprietary IE features. Some of the stuff I've built into gadgets would have had no chance running on another browser :-)
Andy E
A: 

I think using getElementById to lookup the element in this case is not a good approach. This is because of extra steps you have to take to use it. You wrap the element in a DIV, inject in DOM, lookup your element using getElementById and then remove the injected DIV from DOM.

DOM manipulation is expensive and injection might cause unnecessary reflow as well. The problem is that you have a document.getElementById and not a element.getElementById which would allow you to query without injection in the document.

To solve this, using querySelector is an obvious solution which is far more easier. Else, I would suggest using getElementsByClassName if you can and if your element has a class defined.

getElementsByClassName is defined on ELEMENT and hence can be used without injecting the element in DOM.

Hope this helps.

Rajat
@Rajat: `querySelector` is not available in IE7, which is installed by default in Vista and thusly the host for Windows Desktop Gadgets unless IE8 is installed and a gadget specifies IE8 for the `X-UA-Compatible` header in a meta tag. `getElementsByClassName` isn't supported by any currently released version of IE.
Andy E