Jump to content
Larry Ullman's Book Forums

Creating Functions Within A For Loop Error


Recommended Posts

Ok, so I'm attempting to use ajax to bring up the images description when you click on the image thumb nail. To do so, I'm running my function definition through a for loop, replacing select parts of the function to correspond to the image number selected. When it is output in the browser, the code below $(document).ready(function() { is only output once, not 6 times. Does this matter? I'm not nearly as familiar with JS/Jquery as PHP and want to make sure the for loop would be asigning the variable i through six loops.

 

Please also note I have not yet replaced the variable "i" in the for loop because I am still figuring out the syntax for that also. I want to replace galleryImage1 with galleryImage"variable i" and imageId1 with imageId"variable i" so if you could help me with that syntax also, much help appreciated. I figured that part out. I forgot you concatenate in JS with the "+" sign, so the code would be:

 

 <script type="text/javascript">

        $(document).ready(function() {
            for (var i = 1; i <= 6; i++) {
			var classInfoHidden = null;
			
			var stepsVisible = null;
			
		$('#galleryImage'+i+'').mouseout(function() {
			var classInfoHidden = setTimeout(function(){
				$('#image_info').css("visibility" , "hidden");
			},2000);
			
			var defaultVisible = setTimeout(function(){
				$('#default').css("visibility" , "visible");
			},2000);
		});


		$('#galleryImage'+i+'').mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Ecommerce_Site_3/ajax/getimageinfo.php?imageId=' + i, // The URL of the data to fetch.
			data: null, // Don't add any data to the URL.
			dataType:"html"}) // Execute response as a script.
			.done (function(html) {
					clearTimeout(classInfoHidden);
					clearTimeout(stepsVisible);
				$('#default').css("visibility", "hidden");
				$('#image_info').css("visibility", "visible");
				$("#image_info").html(html);

			});
			});
		}
		})
		
Link to comment
Share on other sites

One of the trickiest things with jQuery syntax is keeping all the curly brackets and parentheses straight since you quite often get a lot of nesting with all the anonymous functions being passed as method arguments.

 

When I looked at your code, my first impression was, "Wow! That's a lot of nesting with lots of curly brackets and parentheses. Let's 're-space' that code in an orderly fashion so that I can easily see what does (or doesn't) match up with what."

 

The resulting code I got was the following:

 

$(document).ready(function () {
 
  var classInfoHidden = null;
 
  var stepsVisible = null;
 
  $('#galleryImage1').mouseout(function () {
   
    var classInfoHidden = setTimeout(function () {
     
      $('#class_info').css("visibility", "hidden");
     
    }, 2000);
   
    var stepsVisible = setTimeout(function () {
     
      $('#steps').css("visibility", "visible");
     
    }, 2000);
   
  });
 
  for (var i = 1; i <= 6; i++) {
   
    $('#galleryImage1').mouseover(function () {
     
      $.ajax({
       
        type: "GET",
       
        url: '/Ecommerce_Site_3/ajax/getimageinfo.php?imageId=' + imageId1,
       
        data: null,
       
        dataType: "html"
       
      }).done (function(html) {
       
        clearTimeout(classInfoHidden);
       
        clearTimeout(stepsVisible);
       
        $('#steps').css("visibility", "hidden");
       
        $('#class_info').css("visibility", "visible");
       
        $("#class_info").html(html);
       
      });
     
    });
   
  })
 
}

Once I looked at the code in that state, I noticed that the very last right parenthesis seems to be in the wrong location.

 

I think (although I can't be sure) that if you move the last right parenthesis as follows, your code will work.

 

(Please note that I also added a semicolon at the end, which should be there because it's the end of the ready method call.)

 

$(document).ready(function () {
 
  var classInfoHidden = null;
 
  var stepsVisible = null;
 
  $('#galleryImage1').mouseout(function () {
   
    var classInfoHidden = setTimeout(function () {
     
      $('#class_info').css("visibility", "hidden");
     
    }, 2000);
   
    var stepsVisible = setTimeout(function () {
     
      $('#steps').css("visibility", "visible");
     
    }, 2000);
   
  });
 
  for (var i = 1; i <= 6; i++) {
   
    $('#galleryImage1').mouseover(function () {
     
      $.ajax({
       
        type: "GET",
       
        url: '/Ecommerce_Site_3/ajax/getimageinfo.php?imageId=' + imageId1,
       
        data: null,
       
        dataType: "html"
       
      }).done (function(html) {
       
        clearTimeout(classInfoHidden);
       
        clearTimeout(stepsVisible);
       
        $('#steps').css("visibility", "hidden");
       
        $('#class_info').css("visibility", "visible");
       
        $("#class_info").html(html);
       
      });
     
    });
   
  } // Remove the right parenthesis here...
 
}); // ...And place it here (with a semicolon as well).

Please let me know if that resolves the issue or not.

  • Upvote 1
Link to comment
Share on other sites

I think you edited your post while I was writing mine, so my previous post may be useless. Sorry.

 

Anyway, you're fine with the ready method only being called once.

The point of the ready method in jQuery is to allow you to execute JS/jQuery code sooner than you traditionally are able to using only the window.onload event handler.

As such, you only need to call the ready method once.

 

Within the ready method, you are executing a for loop six times that makes six Ajax requests, which is what you want, so you're fine.

In other words, you jQuery seems fine.

 

Most importantly, are you getting the results you want?

Link to comment
Share on other sites

Unfortunately not, because even though the JS/JQ loop works, it doesn't create the code/function 6 times, it just loops through the function definition 6 times(essentially overwriting itself with each loop) and since those 6 definitions are not output to the browser(my initial fear), the browser only recognizes one script block, with one function definition(the final loop). It was a little experiment that unfortunately didn't work.

 

Luckily there is a pretty easy solution. Since the JS/JQ code is already on a page with PHP on it, I can just run the script block through a PHP for loop six times using the PHP $i variable/string. Bottom line is the browser needs to see six function definitions and that won't happen with a JS, only an PHP echo type loop(or whatever the programming language you use that mirrors echo) .

 

A cleaner way would just be to create a separate PHP include file but since it just experimenting, I just want to make sure it works before I pretty it up lol.

 

Anyway, thanks for the tips and the advice with spacing my code. :)

 

EDIT Just tried running thru a PHP for loop and it works perfectly(I'll post the code when I'm less tired.

 

It was weird though. The JS/JQ loop did work by assigning the event handler to each of the six thumbnails, so it seams it was creating 6 function definitions but for whatever reason, the URL for the ajax request did not recognize the variable i. Very very odd.

Link to comment
Share on other sites

It's because your JS/jQuery is not correct.

Your current JS for loop is attaching an onmouseover event handler to the same element six times.

What you more likely want is the following:

 

 

for (var i = 1; i <= 6; i++) {
 
  $('#galleryImage1').mouseover(function () {

 

to

 

 

for (var i = 1; i <= 6; i++) {
 
  $('#galleryImage' + i).mouseover(function () {

 

Notice the + i part after the '#galleryImage' string.

As an aside, if something like this only works in PHP and not JS, it's not a shortcoming in the language, it means that your JS is incorrect.

  • Upvote 1
Link to comment
Share on other sites

Coupe things. In the code I was using I did substitute the 1 for the variable i like you showed. It did work in the sense that it created 6 events on the six thumbnails. The part I still can't understand is that url part of the $.ajax function didn't work with the i variable. It wasn't being sent/received by the php ajax page. Anyway, I'm sure your right that it must have been something with my code but I'm not going to spend 2 hours looking for it. Thanks for the help. You are the Larry when Larry is out being Larry.

Link to comment
Share on other sites

Sounds like a closure issue. Did you check what value(s) were received by the PHP script? Was it always 6, by chance?

 

If you're not getting any values, then there might be a function scoping issue, but I can't comment without testing your code.

Link to comment
Share on other sites

Coupe things. In the code I was using I did substitute the 1 for the variable i like you showed. It did work in the sense that it created 6 events on the six thumbnails. The part I still can't understand is that url part of the $.ajax function didn't work with the i variable. It wasn't being sent/received by the php ajax page. Anyway, I'm sure your right that it must have been something with my code but I'm not going to spend 2 hours looking for it. Thanks for the help. You are the Larry when Larry is out being Larry.

 

Yes, it makes my life much, much easier, and makes these forums better, when others are able to help out. I appreciate it so.

  • Upvote 1
Link to comment
Share on other sites

  • 3 months later...

Ok, so I am finally back to this. Hartley was absolutely right, this was a closure issue. By the time you "mouseover" the galleryimage, the variable i is already six. So I just spent the last hour trying to figure out how to get the variable i in my Ajax URL to properly assign the right variable for each DOM element in question('#galleryImage')

 

Here is the code in question. I'll shorten it and space it out and add notes to show exactly what I want to do, what I've tried, and how that didn't work.

//I'm trying to create a closure so when the for loop gets to that part of the code
//(where the function would be called), it returns the variable i that exists in that 
//part of the for loop iteration, 1,2,3,4 etc
//(i used n in the function definition to avoid confusion) 

function returnVariable( n ) {
return function() {
return n ;
}
};

			
            for (var i = 1; i <= 5; i++) {
//this part works. the for loop does create a mouseover event for each galleryimage
//(1 through 6)	
		$('#galleryImage'+i+'').mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Jindo_site/ajax/getimageinfo.php?imageId='+ returnVariable(i), 
//this is where I thought the function would work, but instead of returning the 
//variable i that exists in that part of the iteration, it returns the following 
//"http://localhost/Jindo_site/ajax/getimageinfo.php?imageId=function%20()%20{return%20n%20;}"

//this is the net "effect" I want for each loop

$('#galleryImage1).mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Jindo_site/ajax/getimageinfo.php?imageId=1,

//next loop

$('#galleryImage2).mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Jindo_site/ajax/getimageinfo.php?imageId=2,

//next loop

$('#galleryImage3).mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Jindo_site/ajax/getimageinfo.php?imageId=3,			

// and so on...again the  galleryimage part works, but the URL does not. 
//I either get "imageId=6"(if I just use " + i) 
//or 
//"imageId=function%20()%20{return%20n%20;}" if I use the returnVariable() function defined above.

Link to comment
Share on other sites

I'm so mad right now.

I had written out what I thought was one of the best posts I have ever written on this forum, and when I was just about to post it, I accidentally clicked on your avatar, taking me to your profile page and causing my entire post to be deleted.

I'm so depressed.

I honestly don't have the energy to write out the whole post again, so I'm just gonna summarize the problem you are having very briefly here (and if you have any follow-up questions, just ask).

 

Basically, you're right in that what you're experiencing is a closure issue.

The way to solve this problem is by adding a function call within your for loop in order to freeze the various values the i variable has within the for loop at those specific moments in time. In essence, this will allow you to use the i variable like you want to.

 

I kind of wished you had posted all the code for your for loop and ajax method call, as it would make the code I post for my solution make a lot more sense, but anyway, here's the code that you need:

for (var i = 1; i <= 5; i++) {
  
  (function (i) { // Start of the self-invoking function that creates a closure
    
    $('#galleryImage'+i+'').mouseover(function() {
      
      $.ajax({
        
        type: "GET",
        
        url: '/Jindo_site/ajax/getimageinfo.php?imageId=' + i,
        
        // Remaining ajax method call code here
    
  }(i)); // End of the self-invoking function.
  
} // End of for loop

 

Anyway, I'm sorry for not explaining why this works more, but I really am too disheartened to attempt a good explanation right now.

Please let me know what does and doesn't make sense.

Thanks.

Link to comment
Share on other sites

"I'm so mad right now.

I had written out what I thought was one of the best posts I have ever written on this forum, and when I was just about to post it"

 

For hardcore websites like the one i am building this is a point that needs to be discussed in a little more detail. How can we save such information if the user is too make a slight error like the one you have made. The same thing happened to me when i was writing my project diary before on here, some how i had trigger the browser back button and lost my post. If you go to sites like eBay they have some mechanism to prevent things like this from happening.

Link to comment
Share on other sites

You'd just use Ajax or local storage to save the entered text automatically. Gmail's Web interface does this and I'm using Hackpad for something and it's all Ajax. There's no "save" or "publish" option at all.

Link to comment
Share on other sites

I almost have blisters on my fingers already with the amount of coding i have to do. I think the ajax method sounds like a good idea, i worked with localstorage before and its a bit too static to achieve the functionality required by scripts.

 

Have a nice weekend! Edward...

Link to comment
Share on other sites

Edward, local storage is way easier to use than Ajax. The only catch is that local storage isn't support in all browsers.

If I were you, I'd go with something like the following:

if (localStorage) {
  
  // Use local storage.
  
} else {
  
  // Use old-fashioned cookies.
  // These are annoying to deal with in JS, so I'd definitely write some helper functions.
  
}
  • Upvote 1
Link to comment
Share on other sites

Yes its easier that is true, i did write about my usage of it in my project diary. Edward's revamped solution:

if (localStorage) {
  
  // Use local storage.
  
} else {
  
  // Alert message comes up "Well, what can i say, if you had an up to date browser 
  // this wouldn't of happened, so don't even bother complaining"
  
}
Link to comment
Share on other sites

Sure, that's a viable solution.

I think it depends a lot on your crowd.

If your crowd has mostly moved past IE8, then I think it's doable.

 

I'd still do the cookie backup method though.

It's not that difficult to implement, and you'd get some good cookie functions out of it.

Also, I don't think the alert is necessary.

Simply not autosaving the progress should be enough.

Link to comment
Share on other sites

When i get the time to do it i will cover both options, cookies and the localstorage, that should be fun tabular cookies.

 

But the thing is if there is no one on the website is so much code necessary to start of with, i found another viable solution, if someone tries to leave an important page, then just just a js alert message to warn them first before they do leave and loose there information. I think for starters something like that would be find but it also depends on the importance of the script. Last thing we want is people getting charged twice a common error which i noticed on some newly created apps.

 

I just finished my item listing page today, it does everything that ebay does but looks and runs smoother, a hell of a lot of work involved in that. Actually attempting to write localstorage code and cookies would take a considerable amount of time with all the features the listing form covers.

 

Hey larry, i have 300 lines in my controller alone for the item listings page, there are 1000's of lines of code includings javascript, css include files, helper classes, the models i have and about 500 lines on my views file. Seriously i think ive written more code than whats in the whole "PHP and MySQL for Dynamic Web Sites book".

 

I apologize for going a bit off track in this thread, please forgive me i am rather excited!

Link to comment
Share on other sites

  • 3 weeks later...

Ironically I either figured it out that night or the next day. My apologies Hartley for the lost post but you'll always have the memory of the once great post lol. Anyway, it wasn't really me, it was a tutorial I found on closures. It's weird, in PHP, we don't really have to worry much about closures(maybe in more advanced you do). Anyway, here is the code that looks to do something very similar to Hartley's but instead of looping through the function creation, it just loops through the function(could be semantics as they accomplish the same goal:

    <script>
        $(document).ready(function() {

function doCheck(i) {

			var classInfoHidden = null;
			
			var stepsVisible = null;
			
		$('#galleryImage'+i+'').mouseout(function() {
			var classInfoHidden = setTimeout(function(){
				$('#image_info').css("visibility" , "hidden");
			},2000);
			
			var defaultVisible = setTimeout(function(){
				$('#default').css("visibility" , "visible");
			},2000);
		});


		$('#galleryImage'+i+'').mouseover(function() {
			$.ajax({
			type: "GET", // The HTTP request method.
			url: '/Ecommerce_Site_3/ajax/getimageinfo.php?imageId=' + i, // The URL of the data to fetch.
			data: null, // Don't add any data to the URL.
			dataType:"html"}) // Execute response as a script.
			.done (function(html) {
					clearTimeout(classInfoHidden);
					clearTimeout(stepsVisible);
				$('#default').css("visibility", "hidden");
				$('#image_info').css("visibility", "visible");
				$("#image_info").html(html);

			});
			});
}

 for (var i = 1; i <= 5; i++) {
doCheck(i);
 }
		})
		        
</script>


What was so frustrating is that closures are very hard to explain and still really paint a picture in your mind. You just have to see it. The problem seemed to be that the for loop kept overwriting the variable i because the function was NOT being executed, just created. What both Hartley and I's examples do is execute the function with each iteration.

 

Instead of hijacking my own thread, when I get curious, I'll google why PHP generally does have to worry about closures as much(if at all).

 

Here are the two pages I saved that appear to have been my inspiration to unlocking it:

 

http://stackoverflow.com/questions/2687679/jquery-ajax-inside-a-loop-problem

http://www.mennovanslooten.nl/blog/post/62

Link to comment
Share on other sites

giantsfan, yes, your solution is the same as mine. As you said, the semantics are slightly different, but the concept is the same. In both cases, you are calling a function, which is the key.

 

I've thought so many times about closure, and how to explain, but yet, still, it's hard. I've had some success with some people, but certainly, trying to explain it online with only text is hard.

 

Basically, here's the conclusion I've reached:

 

- When you call a function, something is internally created by the JS engine in the browser that's commonly referred to as the call object. The call object contains information about all the arguments passed to the function call as well as the values of those arguments.

 

- In most cases, once a function is executed, all the local variables and parameters are deleted from memory, and that's it. However, if you have an inner function that uses one of those variables/parameters and that inner function is still accessible after the function call ends, or if you have an event handler that may need to reference a variable/parameter at a later time, then the JS engine recognizes that it cannot delete the variable/parameter value from memory (as it may be needed later on).

 

- As a result, the variable/parameter is deleted from memory, making it impossible for you to directly access the associated value after that, but the JS engine internally saves a reference to the value, and uses that value whenever it needs to be known.

 

The reason you see this sort of thing in JS (but not PHP and other mainstream languages) is two-fold:

 

1) JS functions are like any other data type in JS, meaning that they can be defined within other functions. Also, functions are lexically scoped in JS, meaning that functions inherit the scope of where they are defined, not where they are called. As a result, if a function defined within another function references a variable defined within the outer function, then the JS engine has to save that variable value in memory if the inner function is still accessible after the outer function call ends.

 

2) The nature of event-driven languages dictate that you set up event handlers that will call callback functions at a later time. As a result, you are pretty much guaranteeing that at some point, you're going to have to save values that will be referenced at a later, indeterminate time.

 

Anyway, that's the best I can come up with now. Actually, once you understand closure well, you start to realize that all people writing JS use it all the time to their advantage and probably don't even realize it.

 

All the same, I'm glad you solved your problem, and more importantly, I'm really glad that you seem to be understanding closure now. That's a huge step for any JS'er. Congrats.

Link to comment
Share on other sites

 Share

×
×
  • Create New...