views:

35

answers:

1

Dear learned folk, I made a web app that loads images using jquery, ajax and json. I got it to work in Firefox, but alas, Safari and Chrome remain stubborn.

It has to do with a "race condition" where images don't load quickly enough, so I have a load event as well as a trigger event to wait until all images are loaded before appending the html to a container div.

Here is the link to the page that works in FF: http://chereecheree.com/dagworthy/style.html

And some code: var aSectionImages = new Array;

//count how many images are in a section:
var aImagesCount = new Array();

//count how many images of a particular section have been loaded
var aImagesLoaded = new Array();

var htmlString;
var jsonStyleImages = "images.json"
 var jsonNavImages = "imagesNav.json";

//container div:
var scrollableArea = $("#scrollableArea");
$.getJSON(jsonNavImages, getNavIcons);
$.getJSON(jsonStyleImages, makeScroller);


//trigger this function on load event:
function imageLoaded(oImg){
 //get the name of the section of images:
 var locSectionId = (imageInSection(oImg.src)).replace(/\s|'/g, "_");

 //get the file name of the current image
 var locFileName = getFileName(oImg.src);
 if (aImagesLoaded[locSectionId]===undefined){
  aImagesLoaded[locSectionId] = new Array;
 };

 //check if it has already been loaded by seeing if it exists in the array of loaded images:
 var inArray = false;
 inArray = $.inArray(locFileName, aImagesLoaded[locSectionId]);
 if (inArray == -1) {
  //array.push returns the new length of the array:
  var tempLength = aImagesLoaded[locSectionId].push(locFileName);
 }

  if (tempLength==aImagesCount[locSectionId]){
  htmlString += "</div>";
  scrollableArea.append(htmlString);
  //after the html has been appended, force it to be 1000 px --  totally unstable hack.
  scrollableArea.width(1000);
 }
}


//helper function to get section name of a loading image:
function imageInSection(src){
 var resultId=false;
 var locFileName = getFileName(src);
 for (var k = 0; k < aSectionImages.length; k++){
   for(var j=0; j < aSectionImages[k].images.length; j++){
    tempSrc = aSectionImages[k].images[j].src.split("/");
    tempFileName = tempSrc[tempSrc.length-1];
    if (tempFileName == locFileName) {
     resultId = aSectionImages[k].id;
    }
   }
 }
 return resultId;
}

//helper function to get the file name of a loading image:
function getFileName(href){
 var resultFileName=false;
 var locSrc = href.split("/");
 resultFileName = (locSrc[locSrc.length-1]);
 return resultFileName;
}

//function called when ajax request is successful --  it puts together the html string that will be appended to the containter div
function makeScroller(data){
 aSectionImages = data;
 for (ii=0; ii<aSectionImages.length; ii++){
  var locData = aSectionImages[ii]; 
  var locId = locData.id.replace(/\s|'/g, "_");
  aImagesCount[locId] = locData.images.length;
  htmlString = "<div id=\"" + locId + "\">";
   for (jj=0; jj<locData.images.length; jj++){
    var oImage = new Image();
    var locImage = locData.images[jj];

    $(oImage)
     .load(function(){
      imageLoaded(oImage);
     })
     .attr("src", locData.images[jj].src);

     if (oImage.complete && oImage.naturalWidth !== 0){
     $(oImage).trigger("load");
     }

    //alert (oImage.width);
    locImage.id ? locImage.id = " id=\""+locImage.id+"\" " : locImage.id = "";
    htmlString += "<img height=\"" + locImage.height + "\"" + " width=\"" + oImage.width + "\"" + locImage.id + " src=\"" + locImage.src + "\" />";
   }       
 }

} But it's probably best to look at it online, as there is a plugin that's used. Anyhow, the computed style for the container div shows up at "0px" sometimes, which is why I'm forcing it to "1000px" but that hack is not very stable.

Any ideas would be greatly appreciated. Thanks! --Daniel.

A: 

In this section:

$(oImage)
 .load(function(){
  imageLoaded(oImage);
 })
 .attr("src", locData.images[jj].src);

 if (oImage.complete && oImage.naturalWidth !== 0){
 $(oImage).trigger("load");
 }

image.naturalWidth isn't available in all browsers (so undefined !== 0 won't be a correct check), but you don't need it to use it, just rearrange the code, like this:

$(oImage).one('load', function(){ imageLoaded(this); })
         .attr("src", locData.images[jj].src)
         .each(function() {
           if (this.complete) $(this).trigger("load");
         });

This uses .each() to loop through after setting the src and triggers the load event in case it came from cache (this.complete being instantly true indicates this). The .one() call ensures the load event only fires once, whether from cache or a normal load.

Nick Craver
Thanks again, Nick!I'll try that.--Daniel.
D.Arce