views:

182

answers:

4

Hello all,
I'm trying to pass local variables to an inline function in javascript and have that (inline) function manipulate those variables, then be able to access the changed variable values in the containing function. Is this even possible? Here's a sample of the code I'm working with:

function addArtists(artist, request, origElm, xml){

//variables
var artistIdArray = [];

/*coding*/

//traverse xml
$('element', xml).each(function(){

/*coding*/ 

$.ajax({  
   /*parameters*/
   success: function(html) {
    var artistAbb = html;

    /*coding*/

    //add this element's id to the array of ids to make it draggable later
    artistIdArray.push(artistAbb);
    alert(artistIdArray[artistIdArray.length - 1]);

 }//end on success    

});//end ajax call

});//end each(function()

 alert(artistIdArray.length);

}

The problem is I keep getting artistIdArray.length = 0, even though elements are several elements are 'alerted' after they're added to the array.

Like I said, I don't know if it's even possible without global variables or objects. Any ideas? Am I totally wrong?

Edit: Entire function

function addArtists(artist, request, origElm, xml){  

//variables  
var artistIdArray = [];

//create ordered list
//set new <ol>s class
var olClass = "artists"; //holds the class of new <ol>

//create id for new <ol>
var olId = "artists"; //holds the id of new <ol>

//create actual <ol> element
var ol = $('<ol></ol>').attr('id',olId)
                      .addClass(olClass)
                      .appendTo(origElm);

//create the <li> elements from the returned xml
//create class for new <li>s, (just the request minus the 's')
var liClass = request.substring(0, request.length-1);
//traverse xml
$('element', xml).each(function(){

 //create id for new <li> based on artist abbreviation
 var artist = $(this).text();  

 $.ajax({
    url: "php/artistToAbb.php", 
     data: {artist: artist}, 
     dataType: "html", 
     async: true,
     success: function(html) {
    var artistAbb = html;

     //create li     
    var li = $('<li></li>').attr('id', artistAbb)
                           .addClass(liClass)
                           .appendTo(ol);   

    //create arrow icon/button for li
    var img = $('<img />').attr('id', artistAbb + 'ArrowImg')
                          .attr("src","images/16-arrow-right.png")
                          .attr('onclick', "expand(this, '" + artistAbb + "', 'years', 'danwoods')")
                          .addClass("expImg")
                          .appendTo(li);    

    var artistTxt = $('<h2>' + artist + '</h2>')
                       .addClass("artist_txt")
                       .attr('onMouseOver', 'catMouseOver(this)')
                       .attr('onMouseOut', 'catMouseOut(this)')
                       .appendTo(li);

    //tag the ol element's class
    $($(origElm)[0]).addClass('expanded');

    //add this element's id to the array of ids to make it draggable later
    artistIdArray.push(artistAbb);
    alert(artistIdArray[artistIdArray.length - 1]);

  }//end on success     

});//end ajax call

});//end each(function()

//make newly added artist elements draggable
for(var n = 0; n < artistIdArray.length; n++){
  //new Draggable(artistIdArray[n], {superghosting: true, detached: true, onEnd: catClearHighlight});
  alert(artistIdArray[n]);
}
alert(artistIdArray.length);  
}
+4  A: 

UPDATED: Now that you've posted your entire code. The answer is that you shouldn't store the elements in the temporary array at all, but create the draggable for each element as the AJAX call returns.

The problem is that while the array is accessible inside the AJAX callback the code at the end of the function (outside the each) executes before the AJAX calls have completed and so the array is empty. If you create each draggable as the call returns, you don't need the intermediate storage variable and the draggable is created as it is inserted into the DOM. The other alternative, would be to make your AJAX calls synchronous {aSync: false}, but this would also potentially tie up the browser until all of the elements have returned. Better, IMO, to live with the asynchronous nature of the AJAX call and handle each element as it is created.

function addArtists(artist, request, origElm, xml){  

    //create ordered list
    //set new <ol>s class
    var olClass = "artists"; //holds the class of new <ol>

    //create id for new <ol>
    var olId = "artists"; //holds the id of new <ol>

    //create actual <ol> element
    var ol = $('<ol></ol>').attr('id',olId)
                          .addClass(olClass)
                          .appendTo(origElm);

    //create the <li> elements from the returned xml
    //create class for new <li>s, (just the request minus the 's')
    var liClass = request.substring(0, request.length-1);
    //traverse xml
    $('element', xml).each(function(){

            //create id for new <li> based on artist abbreviation
            var artist = $(this).text();            

            $.ajax({
        url: "php/artistToAbb.php", 
                data: {artist: artist}, 
                dataType: "html", 
                async: true,
                success: function(html) {
        var artistAbb = html;

                //create li                   
        var li = $('<li></li>').attr('id', artistAbb)
                               .addClass(liClass)
                               .appendTo(ol);   

        //create arrow icon/button for li
        var img = $('<img />').attr('id', artistAbb + 'ArrowImg')
                              .attr("src","images/16-arrow-right.png")
                              .attr('onclick', "expand(this, '" + artistAbb + "', 'years', 'danwoods')")
                              .addClass("expImg")
                              .appendTo(li);    

        var artistTxt = $('<h2>' + artist + '</h2>')
                           .addClass("artist_txt")
                           .attr('onMouseOver', 'catMouseOver(this)')
                           .attr('onMouseOut', 'catMouseOut(this)')
                           .appendTo(li);

        //tag the ol element's class
        $($(origElm)[0]).addClass('expanded');

        new Draggable(artistAbb, {superghosting: true, detached: true, onEnd: catClearHighlight});

      }//end on success     

    });//end ajax call

    });//end each(function()

}
tvanfosson
But would it be possible to access those modified values in the containing function? after the inner function is done executing the outer function still says artistIdArray.length equals 0...
danwoods
@danwoods: I think the main issue is the asynchronous nature of the Ajax request, when you try to use your array, the Ajax request haven't completed, that's why is empty...
CMS
exactly what I needed. Thanks
danwoods
+1  A: 

You don't need to pass the values at all, just reference them directly in the inline function.

function addArtists(artist, request, origElm, xml){

//variables
var artistIdArray = new Array();
var artistNum = 0;

/*coding*/

//traverse xml  
$('element', xml).each(function(){

                  /*coding*/

    //add this element's id to the array of ids to make it draggable later
    artistIdArray[artistNum] = "some value";
    //alert(artistNum);
    artistNum++;

 }//end on success     

});//end ajax call

});//end each(function()

//test how many elements
 for(var n = 0; n < artistIdArray.length; n++)
   alert(n);

}
Sam
I can get the inner function to read the value of the variables just not modify those values (ie: artistNum is always 0)
danwoods
fixed it part way, see my comment to my own question
danwoods
A: 

Here is a blog post about passing values by reference in javascript:

http://snook.ca/archives/javascript/javascript%5Fpass

Jim Schubert
I am not sure how this applies here. An array is an object in JS.
BalusC
+1  A: 

I can't spot the problem, it probably lies somewhere else than in the as far provided (and edited) code.

But as you're already using jQuery, consider assigning them as "global" jQuery property.

var artistIdArray = [];

would then be

$.artistIdArray = [];

and using it would then look like

$.artistIdArray.push(artistAbb);

you could then at end access it the same way

alert($.artistIdArray.length);
BalusC
Thanks for the info :)
danwoods