junlee Posted January 12, 2011 Share Posted January 12, 2011 Hello, I am learning to use XML and javascript, however I have run into a problem.I am using javascript to read an XML file containing information about a game and put the info into a table for my website, however I am having trouble reading subchildren of subchildren...here is my XML code: <?xml version="1.0" encoding="ISO-8859-1"?><!-- Edited by XMLSpy® --><EQUIP> <SWORD> <PIC>images/swords/sword1.jpg</PIC> <GRADE>Item Shop</GRADE> <NAME>Bad Sword</NAME> <LEVEL>1</LEVEL> <ATTACK>10</ATTACK> <M_ATTACK>7</M_ATTACK> <STATS> <STAT1>2% CRIT</STAT1> <STAT2>8% MOVESPEED</STAT2> </STATS> </SWORD></EQUIP> In this case, I would like to read all of the subchildren of the <STATS> tag (so I don't have to explicitly state how many to look for, I would like to be able to just put all of the subchilds into a table cell regardless of how many).I am using this javascript code: <script type="text/javascript">if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); }else {// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }xmlhttp.open("GET","swords.xml",false);xmlhttp.send();xmlDoc=xmlhttp.responseXML; document.write("<table border='1'>");var x=xmlDoc.getElementsByTagName("SWORD");document.write("<tr><td>Icon</td><td>Name</td><td>Level</td><td>Attack</td><td>Magic Attack</td><td>Stats</td></tr>");for (i=0;i<x.length;i++) { document.write("<tr><td>"); document.write("<img src='"); document.write(x.getElementsByTagName("PIC")[0].childNodes[0].nodeValue); document.write("'>"); document.write("</td><td>"); document.write(x.getElementsByTagName("NAME")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x.getElementsByTagName("LEVEL")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x.getElementsByTagName("ATTACK")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x.getElementsByTagName("M_ATTACK")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x.getElementsByTagName("STAT1")[0].childNodes[0].nodeValue); document.write("<br>"); document.write(x.getElementsByTagName("STAT2")[0].childNodes[0].nodeValue); document.write("<br>"); document.write(x.getElementsByTagName("STAT2")[0].childNodes[0].nodeValue); document.write("</td></tr>"); }document.write("</table>");document.write(s.childNodes.length);</script>[/code] Here is what I am using now, it states exactly how many "STATS" to look for because I was unable to get it to loop through and get subchildren of <STATS>I tried things like: var s = x.getElementsByTagName("STATS");for (j=0;j<s.length;j++) { document.write(s[i][0].childNodes[0].childNodes[0].nodeValue); } Which turns out aren't valid expressions, but I haven't been able to find any documentation on performing the task that I am trying to accomplish.Can anyone help me out? Thanks! Link to comment Share on other sites More sharing options...
boen_robot Posted January 12, 2011 Share Posted January 12, 2011 Each item of s is an element. Each element has a childNodes collection which has it's own length.Looping over inner elements is similar to looping on inner array elements - you need a separate inner loop for each inner element, like: var s = x.getElementsByTagName("STATS");for (var i=0;i<s.length;i++) {/* Loops over all elements of the "s" collection, i.e. all STATS elements */ var ss = s[i].childNodes; for(var j=0;j<ss.length;j++) {/* Loops over all elements of the "ss" collection, i.e. all child nodes of the STATS element in position "i" */ document.write(ss[j].childNodes[0].nodeValue);/* Writes out the value of the first child node of the current node. Assuming the node contains only text, this means you're outputting the text value of the child node at position "j" of the STATS element at position "i" */ }} It's also worth mentioning that whitespace is treated differently in IE8- and other browsers (IE9 included), so as a workaround, you might want to use getElementsByTagName("*") to only tager any descendant elements. Keep in mind that using this shortcut includes descendants i.e. elements on all deeper levels, not just direct children. In your case, it doesn't make a difference though, so you can just have: var s = x.getElementsByTagName("STATS");for (var i=0;i<s.length;i++) {/* Loops over all elements of the "s" collection, i.e. all STATS elements */ var ss = s[i].getElementsByTagName("*"); for(var j=0;j<ss.length;j++) {/* Loops over all elements of the "ss" collection, i.e. all child nodes of the STATS element in position "i" */ document.write(ss[j].childNodes[0].nodeValue);/* Writes out the value of the first descendant element of the current node. Assuming the element contains only text, this means you're outputting the text value of the descendant element at position "j" of the STATS element at position "i" */ }} Link to comment Share on other sites More sharing options...
jeffman Posted January 13, 2011 Share Posted January 13, 2011 If it's possible to change the structure of your XML, life might be easier if1. stat1, stat2 and so on had more specific names that you could search for individually, like <crit> <movespeed> etcor2. if all the stat items used the same tag name (stat) and you could collect them all at once without having to know how many there are. You might even be able to do something like <stat kind="crit"> and the attribute values could simplify the search. Link to comment Share on other sites More sharing options...
junlee Posted January 13, 2011 Author Share Posted January 13, 2011 Thanks for the reply, however it still isn't working I modified the loop in my javascript to: document.write("<tr><td>Icon</td><td>Name</td><td>Level</td><td>Attack</td><td>Magic Attack</td><td>Stats</td></tr>");for (i=0;i<x.length;i++) { document.write("<tr><td>"); document.write("<img src='"); document.write(x[i].getElementsByTagName("PIC")[0].childNodes[0].nodeValue); document.write("'>"); document.write("</td><td>"); document.write(x[i].getElementsByTagName("NAME")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x[i].getElementsByTagName("LEVEL")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x[i].getElementsByTagName("ATTACK")[0].childNodes[0].nodeValue); document.write("</td><td>"); document.write(x[i].getElementsByTagName("M_ATTACK")[0].childNodes[0].nodeValue); document.write("</td><td>"); var s = x.getElementsByTagName("STATS"); for (var p=0;p<s.length;p++) {/* Loops over all elements of the "s" collection, i.e. all STATS elements */ var ss = s[p].childNodes; for(var j=0;j<ss.length;j++) { document.write(ss[j].childNodes[0].nodeValue); document.write("<br>"); } } document.write("</td></tr>"); }document.write("</table>"); However it basically just gets to that part and stops. On my web page, it writes the table just fine until that cell, then it leaves it blank, and doesn't continue on to write the next row either.You can see the result here: http://locohq.com/equipment/equipment.htmland the xml file is: <?xml version="1.0" encoding="ISO-8859-1"?><EQUIP> <SWORD> <PIC>images/swords/sword1.jpg</PIC> <NAME>Bad Sword</NAME> <LEVEL>1</LEVEL> <ATTACK>10</ATTACK> <M_ATTACK>7</M_ATTACK> <STATS> <STAT1>2% CRIT</STAT1> <STAT2>8% MOVESPEED</STAT2> </STATS> </SWORD> <SWORD> <PIC>images/swords/sword2.jpg</PIC> <NAME>Big Sword</NAME> <LEVEL>2</LEVEL> <ATTACK>13</ATTACK> <M_ATTACK>5</M_ATTACK> <STATS> <STAT1>3% CRIT</STAT1> <STAT2>22% MOVESPEED</STAT2> </STATS> </SWORD></EQUIP> It seems like it should work, and I think I tried something like this last night and had the same results. I'm not sure where I am going wrong here. If it's possible to change the structure of your XML, life might be easier if1. stat1, stat2 and so on had more specific names that you could search for individually, like <crit> <movespeed> etcor2. if all the stat items used the same tag name (stat) and you could collect them all at once without having to know how many there are. You might even be able to do something like <stat kind="crit"> and the attribute values could simplify the search.Well I originally had them all named <stat> when I was first trying to get this to work, but later renamed them for my workaround. They are going to be displayed on top of each other in a single cell so at this point it doesn't matter what category (crit, movespeed) they are. I just would like to be able to have a variation in how many stats there are per item, and have them all be displayed in each row's respective STATS cell without having to parse a specific number like I am now. Link to comment Share on other sites More sharing options...
boen_robot Posted January 13, 2011 Share Posted January 13, 2011 Notice carefully that the inner getElementsByTagName() is applied on an element in my example: var ss = s[i].getElementsByTagName("*"); "s" is a collection, and "s" is a member (element) of that collection.In your example, you have var s = x.getElementsByTagName("STATS"); "x" is a collection. You can't apply getElementsByTagName() on a collection. Only over a document or an element.Change that line to: var s = x[i].getElementsByTagName("STATS"); to apply it over a specific element in the "x" collection, and it should work. Link to comment Share on other sites More sharing options...
junlee Posted January 13, 2011 Author Share Posted January 13, 2011 Ah, so the way I had it before was on a collection...so when I ran var ss = s[0].childNodes; it was trying to get the first childNode for two different <STATS> elements, causing an error and causing the script to stop. That makes sense now, it should become easier for me to recognize things like that right away the more I use it :)Thanks for the help! Link to comment Share on other sites More sharing options...
junlee Posted January 14, 2011 Author Share Posted January 14, 2011 hmm actually the problem still isn't solved...I thought it was but it only runs in internet explorer. I noticed this when trying to set up a new file....Here I have a script just testing variables right now to make sure that I am correctly reading the XML: var level = 1; var i = 0; var bonus_2 = x[i].getElementsByTagName("SET_BONUS2")[0].childNodes[0].nodeValue; var bonus_4 = x[i].getElementsByTagName("SET_BONUS4")[0].childNodes[0].nodeValue; var current_set = x[i].getElementsByTagName("LVL" + level); var name = current_set[0].getElementsByTagName("NAME")[0].childNodes[0].nodeValue; var head = current_set[0].getElementsByTagName("HEAD"); var head_pic = head[0].getElementsByTagName("PIC")[0].childNodes[0].nodeValue; var head_pdef = head[0].getElementsByTagName("PHYS_DEF")[0].childNodes[0].nodeValue; var head_mdef = head[0].getElementsByTagName("MAG_DEF")[0].childNodes[0].nodeValue; var head_stats = head[0].getElementsByTagName("STATS"); var grade = x[i].getElementsByTagName("GRADE")[0].childNodes[0].nodeValue; document.write("grade = " + grade + "<br>"); document.write("bonus_2 = " + bonus_2 + "<br>"); document.write("bonus_4 = " + bonus_4 + "<br>"); document.write("head_pic = " + head_pic + "<br>"); document.write("head_pdef = " + head_pdef + "<br>"); document.write("head_mdef = " + head_mdef + "<br>"); var h_stats = head_stats[0].childNodes; document.write(h_stats[0].childNodes[0].nodeValue); Everything works correctly until it gets to the last part with 'var h_stats'. I tried looping using the same code you provided in your examples but that didn't work, so I changed it to try and read just the first node but that didn't work either.Here is the new XML file: <?xml version="1.0" encoding="ISO-8859-1"?><EQUIP><SET> <GRADE>Grade D</GRADE> <LVL1> <NAME>Black Spider</NAME> <SET_BONUS2>set bonus 1</SET_BONUS2> <SET_BONUS4>set bonus 2</SET_BONUS4> <HEAD> <PIC>images/hibachi/d_head.jpg</PIC> <PHYS_DEF>10</PHYS_DEF> <MAG_DEF>7</MAG_DEF> <STATS> <STAT1>99% CRIT</STAT1> <STAT2>99% MOVESPEED</STAT2> </STATS> </HEAD> </LVL1></SET></EQUIP> Any ideas? Chrome is reporting that h_stats[0].childNodes[0] is undefined, IE is printing the text but underlining it, and firefox just isn't showing it at all. Link to comment Share on other sites More sharing options...
boen_robot Posted January 14, 2011 Share Posted January 14, 2011 How is "head" created?You seem to write the code with assuming that it's var head = xmlDoc.getElementsByTagName("head"); i.e. a collection of head elements... is it? Or is it var head = xmlDoc.getElementsByTagName("head")[0]; Could you give a link to the whole page (as you did earlier; I see you're not writing in the same file anymore). Link to comment Share on other sites More sharing options...
junlee Posted January 14, 2011 Author Share Posted January 14, 2011 here ya go: <script type="text/javascript">if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); }else {// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }xmlhttp.open("GET","hibachi.xml",false);xmlhttp.send();xmlDoc=xmlhttp.responseXML; var x=xmlDoc.getElementsByTagName("SET"); var level = 1; var i = 0; var bonus_2 = x[i].getElementsByTagName("SET_BONUS2")[0].childNodes[0].nodeValue; var bonus_4 = x[i].getElementsByTagName("SET_BONUS4")[0].childNodes[0].nodeValue; var current_set = x[i].getElementsByTagName("LVL" + level); var name = current_set[0].getElementsByTagName("NAME")[0].childNodes[0].nodeValue; var head = current_set[0].getElementsByTagName("HEAD"); var head_pic = head[0].getElementsByTagName("PIC")[0].childNodes[0].nodeValue; var head_pdef = head[0].getElementsByTagName("PHYS_DEF")[0].childNodes[0].nodeValue; var head_mdef = head[0].getElementsByTagName("MAG_DEF")[0].childNodes[0].nodeValue; var head_stats = head[0].getElementsByTagName("STATS"); var grade = x[i].getElementsByTagName("GRADE")[0].childNodes[0].nodeValue; document.write("grade = " + grade + "<br>"); document.write("bonus_2 = " + bonus_2 + "<br>"); document.write("bonus_4 = " + bonus_4 + "<br>"); document.write("head_pic = " + head_pic + "<br>"); document.write("head_pdef = " + head_pdef + "<br>"); document.write("head_mdef = " + head_mdef + "<br>"); var h_stats = head_stats[0].childNodes; document.write(h_stats[0].childNodes[0].nodeValue);</script> Link to comment Share on other sites More sharing options...
boen_robot Posted January 14, 2011 Share Posted January 14, 2011 I mentioned this earlier, but perhaps I didn't emphasized it enough... It's also worth mentioning that whitespace is treated differently in IE8- and other browsers (IE9 included), so as a workaround, you might want to use getElementsByTagName("*") to only taget any descendant elements.childNodes includes whitespaces. In IE8-, whitespace nodes are omitted. Your example only works in IE (I assume IE8?) because you're thinking that the first node in the childNodes collection is the first element, but that's only the case in IE8-. Link to comment Share on other sites More sharing options...
junlee Posted January 14, 2011 Author Share Posted January 14, 2011 Yeah the example worked in IE8. Hmm I tried using getElementsByTagName("*") this morning with a for loop but it didn't work, probably was just a syntax error in my code.I just added a quick test using var h_stats = head_stats[0].getElementsByTagName("*"); document.write(h_stats[0].childNodes[0].nodeValue); and var h_stats = head_stats[0].getElementsByTagName("*"); document.write(h_stats[1].childNodes[0].nodeValue); And it seems to be displaying correctly now. Hm I guess most of my confusion with this comes from trying to distinguish 'elements' and 'nodes' when working on the inner tree. That should be fine for now though, I will work on updating the full script when I get home tonight.Thanks again Link to comment Share on other sites More sharing options...
boen_robot Posted January 14, 2011 Share Posted January 14, 2011 In case you're missing on the terminology:Node - any distinct part of an XML document - element is one kind of a node, attribute is another kind of a node, text (whether it's just whitespace or actual text) is a third kind of a node. Contains information that all (or at least most) kind of nodes have such as a "childNodes" collection and the "nodeName" property.Element - one kind of a node (therefore has all information that's part of nodes). Has information specific to it such as an "attributes" collection and the mentioned getElementsByTagName() method, among others.document - another kind of a node (surprisingly; therefore has all information that's part of nodes). Has information specific to it such as the "documentElement" property and the getElementsByTagName() method, among others. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.