4

Im trying to append some JSON data from the last.fm API,

I have been using alert() at several stages to verify the API is being parsed correctly and it is,

This has led me to the conclusion that getElementById().appendChild() doesn't work, below is the URL to the test page I have set up:

http://mutant-tractor.com/tabtest.html

So moving from this straight JS to JQuery I have been advised is the best way to go, That said I have NO clue about anything JQuery and my JS knowledge is based on some basic Java I have done...

How simple/not simple is it to convert this to working JQuery? I would put a bounty on this but I have no idea how to.

function calculateDateAgo(secAgo) {
 var agoString, agoRange, agoScaled;
 if(secAgo >= (agoRange = 60*60*24)) 
   agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"days":"day") + " ago";
 else if(secAgo >= (agoRange = 60*60))
   agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"hours":"hour") + " ago";
 else if(secAgo >= (agoRange = 60))
   agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"minutes":"minute") + " ago";
 else if(secAgo >= -60)
   agoString = "blastin' out now";
 else
   agoString = "soon ;)";
 return agoString;
}

function truncateName(name, l) {
return name.length > l ? name.substr(0,l-2) + "\u2026" : name;
}

function lfmRecentTracks(JSONdata) {

try { 
 var eImg, eLink, eSpan, divTag, eWrapper;
 var oTracks = new Array().concat(JSONdata.recenttracks.track);
 for (var i = 0; i < oTracks.length; i++) {
  //insert coverart image
  var spanTag  = document.createElement("span");
  document.body.appendChild(spanTag);
  spanTag.className = "lfmTrackImageCell tab_item";
  if(oTracks[i].image[1]["#text"] != "") {
   eImg = document.createElement("img");
   spanTag.appendChild(eImg);
   eImg.src = oTracks[i].image[1]["#text"];
   eImg.className = "lfmTrackImage";
  }else{
    eImg = document.createElement("img");
    spanTag.appendChild(eImg);
    eImg.src = "http://cdn.last.fm/flatness/icons/res/3/track.png";
    eImg.className = "lfmTrackImageNotFound";
  }
 }
for (var i = 0; i <>[lessthanhere] oTracks.length; i++) {
  //insert track link
  spanTag = document.createElement("span");
  spanTag.className = "lfmTrackInfoCell tabslider";
  eLink = document.createElement("a");
  eLink.appendChild(document.createTextNode( truncateName(oTracks[i].name, 25) ));
  //alert(truncateName(oTracks[i].name, 25));
  spanTag.appendChild(eLink);
  eLink.href = oTracks[i].url;
  //alert(oTracks[i].url);
  eLink.target = "new";
  eLink.className = "lfmTrackTitle";
  document.body.appendChild(spanTag);

  //insert artist name
  eSpan = document.createElement("span");
  eSpan.appendChild(document.createTextNode(truncateName(oTracks[i].artist["#text"], 22) ));
  //alert(truncateName(oTracks[i].artist["#text"], 22));
  eSpan.className = "lfmTrackArtist";
  document.body.appendChild(eSpan);

  //insert date
  eSpan = document.createElement("span");
  spanTag.appendChild(eSpan);
  eSpan.appendChild(document.createTextNode(   (typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts))  )); 
  //alert((typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts))); 
  eSpan.className = "lfmTrackDate"; 
  document.body.appendChild(eSpan);
 }  
} catch(e) {}
}

The only way the above code works is by using document.body.appendChild()

I have tried calling the script at the bottom of the body and inline so to allow the DOM to be loaded fully but these didn't work.

The div I'm trying to attach them to are 4 different divs i.e. in the for loop each loop needs to reference a different element,

Thanks in advance! Myles

EDIT:

HTML code:

    <link href='css/style.css' rel='stylesheet' type='text/css' />
    <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js'></script>
    <link rel="stylesheet" type="text/css" media="all" href="http://beta.mutant-tractor/wp-content/themes/toolbox/style.css" />
    <link href='/css/tabbedContent.css' rel='stylesheet' type='text/css' />
    <script src="/scripts/JQuery/tabbedContent.js" type="text/javascript"></script>
    <script type="text/javascript" src="/scripts/General/lastfmtest.js"></script>
</head>
<body>
    <aside class="widget">
    <h3>I'm listening to...</h3>

    <div class="tabbed_content">
        <div id="lfmArtRow" class="tabs">
            <div class="moving_bg">
                &nbsp;
            </div>
            <script type="text/javascript" src="http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;user=mutant_tractor&amp;api_key=6b85edc3841f3d0935d9dfed9c556634&amp;limit=4&amp;format=json&amp;callback=lfmRecentTracks"></script>
        </div>
    </aside>
    <div id="lfmRecentTracks" style='padding: 15px;'>
        <div class="content">
            <div class="tabbed_content">
                <div class="slide_content">                     
                    <div class="tabslider">
                        <div class="sliding_info">Test 1
                        </div>
                        <div class="sliding_info">Test 2
                        </div>
                        <div class="sliding_info">Test 3
                        </div>
                    <div class="sliding_info">Test 4
                        </div>
                    </div>
                    <br style="clear: both" />
                </div>
            </div>
        </div>
    </div>
    <div id="test">
        </div>
</body>

Myles Gray
  • 8,711
  • 7
  • 48
  • 70
  • "I would put a bounty..." You haven't [yet earned that privilege](http://stackoverflow.com/privileges/set-bounties). You need at least 75. – Conrad Frix Jan 24 '11 at 15:21
  • 6
    It would be very helpful to see the relevant HTML markup. There's no reason that "plain JavaScript" shouldn't work, and the advice to convert everything to jQuery sounds well-intentioned but questionable, especially given the complete lack of familiarity you claim to have with the framework. – Pointy Jan 24 '11 at 15:25
  • Although JavaScript lets you get away with not doing so, ALWAYS end lines with semicolons. You have to be stricter than JS lets you be if you want to stay sane. – Jonathon Faust Jan 24 '11 at 15:29
  • 2
    Unexplained downvotes are such a drag. – Pointy Jan 24 '11 at 15:43
  • @Pointy - HTML code added to OP. – Myles Gray Jan 24 '11 at 15:45
  • 1
    And note that JavaScript is a VERY different beast than Java, even though it has Java in the name and the syntax is superficially similar. The overlap of names was merely marketing by Netscape and Sun. – adamjford Jan 24 '11 at 15:47

5 Answers5

1

document.getElementById() and appendChild() in general work fine in all major browsers, apart from a (likely irrelevant) issue with IE using id and name interchangeably. jQuery isn't necessary here.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
1

There's no reason to do this. jQuery is JavaScript, but with added functionality. If it doesn't work in JavaScript, the translated jQuery wont work either. That's not even mentioning the added (unnecessary) work to translate it.

Sparafusile
  • 4,696
  • 7
  • 34
  • 57
  • jQuery adds a layer of browser compatibility. Because the OP is using DOM manipulation, it's definitely a good goal to use a framework to make it work in all browsers (especially IE) without becoming insane. – acme Jan 24 '11 at 16:33
  • getElementById and appentChild are both supported by all major browsers. – Sparafusile Jan 24 '11 at 16:38
1

Instead of

document.body.appendChild( ... );

have your function locate the <div> where you want the content added, like this:

var targetDiv = document.getElementById('lfmRecentTracks');

(Do make sure that there's only one element on the page with that "id" value!! Also, for the sake of IE, make sure that name isn't a "name" attribute on anything either.)

Then you should be able to do this:

targetDiv.appendChild( whatever );
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Hi Pointy, In FireBug I keep getting TargetDiv is null? Even though it is clearly defined, any idea why this would be? – Myles Gray Jan 24 '11 at 16:56
  • Make sure that your code runs *after* the target `
    ` element has been parsed by the browser. That means putting your code either in a function bound to the window "load" event, or else putting (or importing) the script at the very end of the page body.
    – Pointy Jan 24 '11 at 17:00
  • Also, in the Firebug console you should be able to directly type "document.getElementById('lfmRecentTracks')" and get a result – Pointy Jan 24 '11 at 17:01
  • Thanks man, I'm just trying to figure out how to assign an individual ID to each div so that the script will put them into the right one based on the loop it is on, e.g. run a for() loop that has an internal count that will then target a different id based on the loop, var count = 0; for(){count = count + 1; document.getElementById('sliding_info'+count);} something like that. If you could target classes life would be good... – Myles Gray Jan 24 '11 at 18:08
  • Hmm well that sounds like a whole different thing than what you posted here. Maybe that calls for another question, or you could update this one. – Pointy Jan 24 '11 at 18:09
  • Seperate question here: http://stackoverflow.com/questions/4785648/targeting-class-name-iterations-or-ids-based-on-count-in-for-loop – Myles Gray Jan 24 '11 at 18:30
1

I wont argue whether you need jQuery or not. It's not a necessity. However, if you do want to use it, this is how you'd do the same code with jQuery:

            function calculateDateAgo(secAgo) {
             var agoString, agoRange, agoScaled;
             if(secAgo >= (agoRange = 60*60*24)) 
               agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"days":"day") + " ago";
             else if(secAgo >= (agoRange = 60*60))
               agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"hours":"hour") + " ago";
             else if(secAgo >= (agoRange = 60))
               agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"minutes":"minute") + " ago";
             else if(secAgo >= -60)
               agoString = "blastin' out now";
             else
               agoString = "soon ;)";
             return agoString;
            }

            function truncateName(name, l) {
            return name.length > l ? name.substr(0,l-2) + "\u2026" : name;
            }

            function lfmRecentTracks(JSONdata) {
                try { 
                    var eImg, eLink, eSpan, divTag, eWrapper, date;
                    var oTracks = new Array().concat(JSONdata.recenttracks.track);
                    for (var i = 0; i < oTracks.length; i++) {
                        //insert coverart image
                        var spanTag  = $('<span class="lfmTrackImageCell tab_item"></span>');
                        $("body").append(spanTag);
                        if(oTracks[i].image[1]["#text"] != "") {
                            eImg = $('<img src="'+oTracks[i].image[1]["#text"]+'" class="lfmTrackImage" />');
                            spanTag.appendChild(eImg);
                        }else{
                            eImg = $('<img src="http://cdn.last.fm/flatness/icons/res/3/track.png" class="lfmTrackImageNotFound" />');
                            spanTag.appendChild(eImg);
                        }
                     }
                    for (var i = 0; i <>[lessthanhere] oTracks.length; i++) {
                      //insert track link
                        spanTag = $('<span class="lfmTrackInfoCell tabslider"><a href='+oTracks[i].url+' target="new" class="lfmTrackTitle">'+truncateName(oTracks[i].name, 25)+'</a></span>');
                        //alert(truncateName(oTracks[i].name, 25));
                        //alert(oTracks[i].url);
                        $("body").append(spanTag);

                        //insert artist name
                        eSpan = $('<span class="lfmTrackArtist">'+truncateName(oTracks[i].artist["#text"], 22)+'</span>');
                        //alert(truncateName(oTracks[i].artist["#text"], 22));
                        $("body").append(eSpan);

                        //insert date
                        date = (typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts));
                        eSpan = $('<span class="lfmTrackDate">'+ date +'</span>');
                        spanTag.append(eSpan);
                        //alert((typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts))); 
                        $("body").append(eSpan);
                     }
                } catch(e) {}
            }

Now, I offer no warranty to whether this works or not in production, but it looks right. Also, I wasn't sure what you were doing with the i <>[lessthanhere] oTracks.length; section, so I left it in. Normally, it'd be i < oTracks.length;.

Adrian Gonzales
  • 1,010
  • 6
  • 8
  • Thanks Adrian,I'll have a look over it, the purpose of that code was to stop SO from ending the code section there... It does that for some reason when I enter a less than sign. – Myles Gray Jan 24 '11 at 16:08
0

It should be pretty simple. If there is one thing jQuery is good at (and there are many), it is DOM manipulation (ie exactly what you're trying to do.) Start with these tutorials

http://docs.jquery.com/Tutorials:How_jQuery_Works

http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery

The second one will give you a good introduction to inserting HTML into a document. After that just check out the manipulation section of the documentation.

Vadim
  • 17,897
  • 4
  • 38
  • 62