4

(I ask my question again after the first one was terribly formulated)

I face the following problem:

<div class="testA" id="test1"></div>

The above written element is predefined. I now load a xml tree via XMLHttpRequest & Co. delivering the following response:

<response>
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</response>

I now append the first div using

request.responseXML.getElementsByTagName("response")[0]
                       .getElementsByTagName("div")[0]

into the predefined div

<div class="testA" id="test1">

The final document looks like this (checked using development tools):

<div class="testA" id="test1">
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</div>

When I now try to get the element <div class="colorSelector" id="0-0"> using getElementById("0-0") I get the expected result.

But using getElementsByClassName("colorSelector") returns [].

Did I miss something? Is it probably a leftover of the fact the nodes were of type Element and not HTMLElement?

pimvdb
  • 151,816
  • 78
  • 307
  • 352
Leo Selig
  • 1,062
  • 1
  • 16
  • 30
  • Are the nodes actually commented out? If not, what is the context from which you're calling `getElementsByClassName`? – user113716 Sep 30 '11 at 17:06
  • 1
    The real question is, why would `GetElementById` be getting something that is commented out? Are you sure it is returning something? – ThatMatthew Sep 30 '11 at 17:15
  • 1
    @ThatMatthew: OP has verified that `.getElementsByClassName` returns `[]`, which probably means the console is being used to verify. I'd guess that either the code comments aren't actually there, or there's another element on the page with the same ID. – user113716 Sep 30 '11 at 17:21
  • Yes, those are my two guesses as well. – ThatMatthew Sep 30 '11 at 17:27
  • Is there any reason why this topic is tagged as XML ? I guess you'll need to provide a demo, there are only 2 options so far: both method return results or none. – Dr.Molle Sep 30 '11 at 17:33
  • @Dr.Molle: No, there's a third option where `gEBI` returns results and `gEBC` doesn't. See the top [of my answer](http://stackoverflow.com/questions/7613621/getelementsbyclassname-doesnt-work-properly-at-element-nodes/7613805#7613805). – user113716 Sep 30 '11 at 17:42
  • @LeoSelig: If you're going to ask a question, seriously consider sticking around and responding to the people who are tying to help you. – user113716 Sep 30 '11 at 17:43
  • I'm feeling a little trolled here. A brand new account asks a confusingly-written question, then doesn't respond to any of the people who are trying to help. – ThatMatthew Sep 30 '11 at 19:06
  • Do you mean it returns an array or an empty array? Getting elements by classname usually returns multiple elements. Try `getElementsByClassName("colorSelector")[0]`. – mowwwalker Sep 30 '11 at 19:33
  • Actually, might be something to do with parsing the xml before using it. Try looking at something like: http://www.hiteshagrawal.com/javascript/javascript-parsing-xml-in-javascript – mowwwalker Sep 30 '11 at 19:38

6 Answers6

3

colorSelector is commented out. JavaScript only works within the DOM, and commented out portions aren't in the DOM.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
1

Since you said that your getElementById("0-0") is successful, then clearly you don't actually have the nodes commented out.

I'm guessing you're doing:

document.getElementById("0-0").getElementsByClassName('colorSelector');

...which will not work because the element selected by ID does not have any descendants with that class.


Since you show HTML comments in the markup, I'd also wonder if you have some different element on the page with the ID "0-0". Take a look for that.


If your nodes are actually commented out, you'll need to first select the comment, and replace it with the markup contained inside:

var container = document.getElementById('test1'),
    comment = container.firstChild;

while( comment && comment.nodeType !== 8 ) {
    comment = comment.nextSibling;
}

if( comment ) {
    container.innerHTML = comment.nodeValue;
}

...resulting in:

<div class="testA" id="test1">
    <div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>
</div>

...but there again, this doesn't seem likely since your getElementsById does work.

user113716
  • 318,772
  • 63
  • 451
  • 440
0
<!--<div class="colorSelector" id="0-0">
        <div class="gbSelector" id="1-0">
            <table style="none" id="2-0"></table>
        </div>
    </div>-->

The above code is gray for a reason: it's a comment. Comments aren't parsed by the browser at all and have no influence on the page whatsoever.

You'll have to parse the HTML, read the comments, and make a new DOM object with the contents of the comment.

Blender
  • 289,723
  • 53
  • 439
  • 496
0

Please describe what you are doing with the returned results. There is a significant difference between a nodeList and a node, nodeLists are LIVE.

So if you assign a nodeList returned by getElementsByClassName() (or similar) to a variable, this variable will change when you remove the nodes inside the nodeList from the DOM.

Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • i have a function like this: handleTmpl = function(tmplNode) { element = parentEl.appendChild($t("div", 0, tmplNode)); var bgPixels = element.getElementsByClassName("gbSelector")[0]; } where tmplNode is the node received by processing the response now element.getElementsByClassName("gbSelector")[0] returns undefined although the development tools clearly show the complete tree in the code – Leo Selig Sep 30 '11 at 18:46
  • That's confusing now, what node do you like to access, `.gbSelector` or `.colorSelector`? However, see once more the answer by Ӫ_._Ӫ . `element` will be `#0-0` inside your function(appendChild returns the element that has been appended), so `element.getElementsByClassName("colorSelector")` will not return anything, because `.colorSelector` is not a child of `element`, it's `element` itself. – Dr.Molle Oct 01 '11 at 01:08
0

I now append the first div

How do you do that? What you have in the responseXML are XML elements, and not HTML elements.

  • You shouldn't be able to appendChild them into a non-XHTML HTML document;

  • actually you shouldn't be able to appendChild them into another document at all, you're supposed to use importNode to get elements from one document to another, otherwise you should get WRONG_DOCUMENT_ERR;

  • even if you managed to insert them into an HTML due to browser laxness, they're still XML elements and are not semantically HTML elements. Consequently there is nothing special about the class attributes; just having that name doesn't make the attribute actually represent a class. getElementsByClassName won't return elements just because they have attributes with the name class; they have to be elements whose language definition associates the attributes with the concept of classness (which in general means HTML, XHTML or SVG).

(The same should be true of the id attributes; just having an attribute called id doesn't make it conceptually an ID. So getElementById shouldn't be working. There is a way to associate arbitrary XML attributes with ID-ness, which you don't get with class-ness, by using an <!ATTLIST declaration in the doctype. Not usually worth bothering with though. Also xml:id is a special case, in implementations that support XML ID.)

You could potentially make it work if you were using a native-XHTML page by putting suitable xmlns attributes on the content to make it actual-XHTML and not just arbitrary-XML, and then using importNode. But in general this isn't worth it; it tends to be simpler to return HTML markup strings (typically in JSON), or raw XML data from which the client-side script can construct the HTML elements itself.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • thank you very much, I thought it would be a consequence of mixing up xml with html nodes but I didn't know exactly why – Leo Selig Sep 30 '11 at 20:18
  • another question to that: how do you thinkt i should construct the html elements from a string (i prefer solutions without innerHTML) or xml data (do you think of a complete "recreation" of the xml nodes as html elements?) – Leo Selig Oct 01 '11 at 08:58
  • `importNode` should be able to recreate XML nodes as HTML nodes but there are browser difficulties (especially IE where you don't even get the method). I don't think I'd try to put HTML elements inside XML as elements; the languages are just too different, unless you do your entire app in XHTML (in which case you have problems with IE<9). So you would, I think, have to use either strings (in XML encoded as `<div class=...` or in a JSON response). – bobince Oct 01 '11 at 09:09
  • If you don't like using `innerHTML` then the answer is not to generate the markup at the server side but instead pass back the simple data (in XML as something like `1red` or JSON for compactness), and then use DOM methods to create the necessary divs and tables. – bobince Oct 01 '11 at 09:11
  • Okay, thanks a lot! It seems like there is no real alternative to innerHTML so I think I will use it. @bobince I used a method like this also when processing AJAX requests, but in this case the idea is to post load template files via AJAX so this doesn't work, but nevertheless thank you! – Leo Selig Oct 01 '11 at 09:28
0

Here's a way to do it for Firefox, Opera, Chrome and Safari. Basically, you just do div.innerHTML = div.innerHTML to reinterpret its content as HTML, which will make that class attribute from the XML file be treated as an HTML class name.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title></title>
        <script>
            window.addEventListener("DOMContentLoaded", function() {
                var div = document.getElementsByTagName("div")[0];
                var req = new XMLHttpRequest();
                req.onreadystatechange = function() {
                    if (this.readyState === 4 && this.status === 200) {
                        var doc = this.responseXML;
                        div.appendChild(document.importNode(doc.getElementsByTagName("response")[0].getElementsByTagName("div")[0], true));
                        div.innerHTML = div.innerHTML;
                        alert(document.getElementsByClassName("colorSelector").length);
                    }
                };
                req.open("GET", "div.xml");
                req.send();
            }, false);
        </script>
    </head>
    <body>
        <div class="testA"></div>
    </body>
</html>

Remove the this.status === 200 if you're testing locally in browsers that support xhr locally.

The importNode() function doesn't seem to work in IE (9 for example). I get a vague "interface not supported" error.

You could also do it this way:

var doc = this.responseXML;
var markup = (new XMLSerializer()).serializeToString(doc.getElementsByTagName("response")[0].getElementsByTagName("div")[0]);
div.innerHTML = markup;

as long as the markup is HTML-friendly as far as end tags for empty elements are concerned.

Shadow2531
  • 11,980
  • 5
  • 35
  • 48
  • i tried this but confusingly I still just get [] returned when using document.getElementsByClassName("colorSelector") whereas it works fine using document.getElementById("0-0") – Leo Selig Oct 01 '11 at 08:55
  • @Leo Selig, I accidentally had this.responseXML.document instead of just this.responseXML. It works in FF, Opera, Chrome and Safari for me. Doesn't seem to work in IE9 though. Something with the importNode maybe. – Shadow2531 Oct 01 '11 at 14:36
  • @Leo Selig, I added another way to do it. – Shadow2531 Oct 01 '11 at 14:56