Tricky Javascript Looping

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • npm
    New Member
    • Apr 2007
    • 57

    Tricky Javascript Looping

    I've got an XML list of radio stations that I want to display, but I think it involves some nested FOR loops that I can't quite figure out.

    Here's a sample of my XML:
    Code:
    <stations>
    
    	<state id="California">
    		<station city="City 1" freq="88.1 FM" />
    		<station city="City 2" freq="88.7 FM" />
    		<station city="City 3" freq="89.3 FM" />
    		<station city="City 4" freq="100.3 FM" />
    	</state>
    	
    	<state id="Oregon">
    		<station city="City 1" freq="91.1 FM" />
    		<station city="City 2" freq="90.1 FM" />
    	</state>
    	
    	<state id="Washington">
    		<station city="City 1" freq="95.7 FM" />
    		<station city="City 2" freq="93.1 FM" />
    		<station city="City 3" freq="90.7 FM" />
    	</state>
    
    </stations>
    After browsing through w3schools.com's XML DOM section, I think I found a script that is close to what I need (http://www.w3schools.com/dom/tryit.a...y_dom_nodetype), and here's my version that isn't quite working:
    Code:
    <script type="text/javascript">
    	xmlDoc = loadXMLDoc("xml/stations.xml");
    	x = xmlDoc.getElementsByTagName('state');
    	for (i=0; i<x.length; i++) {
    		y = x.childNodes;
    		document.write("<b>" + x[i].getAttribute('id') + "</b><br />");
    		for (z=0; z<y[i].length; z++) {
    			document.write(y[i].childNodes[z].getAttribute('city'));
    			document.write(", ");
    			document.write(y[i].childNodes[z].getAttribute('freq'));
    			document.write("<br />");
    			}
    		document.write("<br /><br />");
    	}
    </script>
    What I'm trying to accomplish is to have it display the state name once and then each city's name & frequency below its state's name, like this:

    California
    City 1, 88.1 FM
    City 2, 88.7 FM
    City 3, 89.3 FM
    City 4, 100.3 FM

    Oregon
    City 1, 91.1 FM
    City 2, 90.1 FM

    Washington
    City 1, 95.7 FM
    City 2, 93.1 FM
    City 3, 90.7 FM

    Any help you have would be greatly appreciated. Thanks in advance!
  • Dormilich
    Recognized Expert Expert
    • Aug 2008
    • 8694

    #2
    one problem you have is using childNodes. mind that a child node is not only an element, even whitespace between elements counts as child node! you’re better off with getElementsByTa gName().

    now to the main problem, x and y represent independent NodeLists (very simple arrays), so take care where you need an index (and which variable goes there).

    report back if you can’t see what I mean.

    PS: using FireBug will help you a great deal here

    side note: it is recommended to display the results using DOM methods (or .innerHTML) rather than through document.write( )

    Comment

    • npm
      New Member
      • Apr 2007
      • 57

      #3
      Hi, thanks for the response.

      I can see how using getElementsByTa gName() would directly access the <station> nodes instead of using childNodes, but I don't see how I would display just the ones that go with each state. Would that still use some sort of nested FOR loops, or maybe IF statements? I guess that's where my misuse of the x and y variables is leading me astray.

      I did switch to using innerHTML, at least to just list the states:
      Code:
      <div id="stationList"></div>
      <script type="text/javascript">
      	xmlDoc = loadXMLDoc("xml/stations.xml");
      	x = xmlDoc.getElementsByTagName('state');
      	for (i=0; i<x.length; i++) {
      		document.getElementById("stationList").innerHTML = "<b>" + x[i].getAttribute('id') + "</b><br />";
      	}
      </script>
      But when I ran it in the browser (Firefox 3), it only displayed the last state, not all of them in a list.

      I installed Firebug and I could find where my variable x had all the states, but I'm not sure why only the last one was displayed and, being new to Firebug, I'm not sure yet on how to fix it.

      Thanks again, in advance!

      Comment

      • Dormilich
        Recognized Expert Expert
        • Aug 2008
        • 8694

        #4
        Originally posted by npm
        I can see how using getElementsByTa gName() would directly access the <station> nodes instead of using childNodes, but I don't see how I would display just the ones that go with each state.
        access the <state>s like you access the <station>s. from a general point of view, they are the same.

        Originally posted by npm
        Would that still use some sort of nested FOR loops, or maybe IF statements? I guess that's where my misuse of the x and y variables is leading me astray.
        your original code worked all right after fixing the childNodes, x and y bugs. and yes, you need nested loops (because you have nested data (of cause there are ways to work around that, but I don’t consider that better… yet))

        Originally posted by npm
        I installed Firebug and I could find where my variable x had all the states, but I'm not sure why only the last one was displayed and, being new to Firebug, I'm not sure yet on how to fix it.
        try out the script tab, there you can set breakpoints (by clicking just before a line number and Firebug will stop there in the next run so you can inspect each of the variables (and for instance see why a loop is skipped)

        Comment

        • npm
          New Member
          • Apr 2007
          • 57

          #5
          Here's my next trial (and error):
          Code:
          <div id="stationsState"></div>
          <div id="stationsCity"></div>
          <script type="text/javascript">
          	xmlDoc = loadXMLDoc("xml/stations.xml");
          	x = xmlDoc.getElementsByTagName('state');
          	y = xmlDoc.getElementsByTagName('station');
          	for (i=0; i<x.length; i++) {
          		document.getElementById("stationsState").innerHTML = "<b>" + x[i].getAttribute('id') + "</b><br />";
          		for (z=0; z<y.length; z++) {
          			document.getElementById("stationsCity").innerHTML = y[z].getAttribute('city') + "<br />";
          		}
          	}
          </script>
          What I got was only the last state listed and the last city in that state listed under it:

          Washington
          City 3

          So...I still don't know why it's only listing the last one in the list, and I'm having trouble navigating through Firebug to find the reason.

          Also, I tried using the document.write method (just to see how it worked):
          Code:
          <script type="text/javascript">
          	xmlDoc = loadXMLDoc("xml/stations.xml");
          	x = xmlDoc.getElementsByTagName('state');
          	y = xmlDoc.getElementsByTagName('station');
          	for (i=0; i<x.length; i++) {
          		document.write("<b>" + x[i].getAttribute('id') + "</b><br />");
          		for (z=0; z<y.length; z++) {
          			document.write(y[z].getAttribute('city') + "<br />");
          		}
          	}
          </script>
          What I got was each state, followed by EVERY city:

          California
          City 1
          City 2
          City 3
          City 4
          City 1
          City 2
          City 1
          City 2
          City 3
          Oregon
          City 1
          City 2
          City 3
          City 4
          City 1
          City 2
          City 1
          City 2
          City 3
          Washington
          City 1
          City 2
          City 3
          City 4
          City 1
          City 2
          City 1
          City 2
          City 3

          So, this is closer to what I need, but I'm still missing something...

          Comment

          • Dormilich
            Recognized Expert Expert
            • Aug 2008
            • 8694

            #6
            Originally posted by npm
            Here's my next trial (and error):
            Code:
            	for (i=0; i<x.length; i++) {
            		document.getElementById("stationsState").innerHTML = "<b>" + x[i].getAttribute('id') + "</b><br />";
            		for (z=0; z<y.length; z++) {
            			document.getElementById("stationsCity").innerHTML = y[z].getAttribute('city') + "<br />";
            		}
            that’s because you overwrite the .innerHTML every time you pass the loop. use the concatenation operator:
            Code:
            var div.innerHTML [U]+=[/U] "some text, ";
            Originally posted by npm
            Code:
            <script type="text/javascript">
            	xmlDoc = loadXMLDoc("xml/stations.xml");
            	x = xmlDoc.getElementsByTagName('state');
            	[u]y = xmlDoc.getElementsByTagName('station');[/u]
            	for (i=0; i<x.length; i++) {
            		document.write("<b>" + x[i].getAttribute('id') + "</b><br />");
            		for (z=0; z<y.length; z++) {
            			document.write(y[z].getAttribute('city') + "<br />");
            		}
            	}
            </script>
            What I got was each state, followed by EVERY city:
            of course… y is a NodeList of all <station>s, not just the <station>s of one <state>

            tip: improve the original code

            note: getElement***() works with every element, not just document

            Comment

            • Jezternz
              New Member
              • Jan 2008
              • 145

              #7
              Would this work?

              Code:
              <script type="text/javascript">
                  xmlDoc = loadXMLDoc("xml/stations.xml");
                  x = xmlDoc.getElementsByTagName('state');
                  for (i=0; i<x.length; i++) {
                      document.write("<b>" + x[i].getAttribute('id') + "</b><br />");
                      y = x[i].getElementsByTagName('station');
                      for (z=0; z<y.length; z++) {
                          document.write(y[z].getAttribute('city') + "<br />");
                      }
                  }
              </script>

              Comment

              • Dormilich
                Recognized Expert Expert
                • Aug 2008
                • 8694

                #8
                does it work? ;)

                .....

                Comment

                • npm
                  New Member
                  • Apr 2007
                  • 57

                  #9
                  Almost...this is what I got:

                  California
                  null
                  null
                  null
                  null
                  Oregon
                  null
                  null
                  Washington
                  null
                  null
                  null

                  So, it's listing the right number of stations, just not the actual names.

                  Comment

                  • npm
                    New Member
                    • Apr 2007
                    • 57

                    #10
                    Wait, I had something mis-typed. It works fine!

                    Thank you, both!

                    Comment

                    • Dormilich
                      Recognized Expert Expert
                      • Aug 2008
                      • 8694

                      #11
                      are you interested in some further lecture?

                      Comment

                      • npm
                        New Member
                        • Apr 2007
                        • 57

                        #12
                        Actually, yes...
                        I'm still interested in also making it work with innerHTML. I tried just replacing:
                        Code:
                        document.getElementById("divName").innerHTML = "<b>" + x[i].getAttribute('id') + "</b><br />";
                        with:
                        Code:
                        document.getElementById("divName").innerHTML += "<b>" + x[i].getAttribute('id') + "</b><br />";
                        but it actually crashed my browser.

                        I didn't get where to put the code you suggested:
                        Code:
                        var div.innerHTML += "some text, ";

                        Comment

                        • Dormilich
                          Recognized Expert Expert
                          • Aug 2008
                          • 8694

                          #13
                          Originally posted by npm
                          but it actually crashed my browser.
                          well, didn’t happen to me. all going smooth…

                          Originally posted by npm
                          I didn't get where to put the code you suggested:
                          Code:
                          var div.innerHTML += "some text, ";
                          ok, the var keyword was stupid… I’m a human after all

                          Code:
                          var div = document.getElementById("divName");
                          var x = xmlDoc.getElementsByTagName('state');
                          for (i=0; i<x.length; i++) {
                          	div.innerHTML += "<b>" + x[i].getAttribute('id') + "</b><br />";
                          	y = x[i].getElementsByTagName('station');
                          	for (z=0; z<y.length; z++) {
                          		div.innerHTML += y[z].getAttribute('city') + "<br />";
                          	}
                          }

                          Comment

                          Working...