I deployed a Node.js application called Magic Date Ball this week. On the index page, there’s a slideshow of GIFs stored in a JavaScript array. A traditional loop iterates over an array almost instantaneously – certainly way faster than I wanted my GIFs to appear. I wanted to pause each iteration of the loop, thereby creating a slideshow, which sounds easy, but was slightly more complicated than anticipated.
The solution seemed to be to include a setTimeout function inside the loop, which is as it sounds – it’s a function that executes at set intervals.
While this seemed like an easy solution, there were two issues I faced. First, the array I wished to loop through was inaccessible inside the setTimeout function due to JavaScript’s scoping issues. Therefore, the value of aboutArr[i] in the below code was undefined:
var aboutArr = [$('#clueless1').html(), $('#clueless2').html(), $('#clueless3').html(), $('#clueless4').html(), $('#clueless5').html(), $('#clueless6').html(), $('#clueless7').html(), $('#help').html()]; function clueless() { for (var i in aboutArr) { setTimeout(function() { $('.clueless').html(aboutArr[i]); }, 2500 * i); } } clueless();
To resolve the scoping issue, I initially set a placeholder variable inside the loop to which the aboutArr element would be passed. The placeholder variable was accessible inside the inner function.
However, with each iteration, a new value was assigned to placeholder, which resulted in this variable being set to the final value of aboutArr. This resulted in the last aboutArr element being the only element rendered by the browser.
var aboutArr = [$('#clueless1').html(), $('#clueless2').html(), $('#clueless3').html(), $('#clueless4').html(), $('#clueless5').html(), $('#clueless6').html(), $('#clueless7').html(), $('#help').html()]; function clueless() { for (var i in aboutArr) { var placeholder = aboutArr[i]; setTimeout(function() { $('.clueless').html(placeholder); }, 2500 * i); } } clueless();
After thinking about this for a while and reviewing potential solutions online, the answer became clear. If you want to access each array item inside the inner loop, you have to not only create a variable to which to pass each aboutArr element; that variable needs to be a second array. This way, each aboutArr element is accessible inside the inner function, instead of whatever element was passed during the final iteration.
I’m sure there are several ways to accomplish this, but here’s what worked for me:
var aboutArr = [$('#clueless1').html(), $('#clueless2').html(), $('#clueless3').html(), $('#clueless4').html(), $('#clueless5').html(), $('#clueless6').html(), $('#clueless7').html(), $('#help').html()]; var placeholderArr = []; function clueless() { for (var i in aboutArr) { placeholderArr[i] = aboutArr[i]; var j = 0; setTimeout(function() { $('.clueless').html(placeholderArr[j]); j += 1; }, 2500 * i); } } clueless();
Line 1: aboutArr I hope to pause on each iteration.
Line 5: empty placeholderArr in which the value from aboutArr will be passed on each iteration.
Line 8: for loop which will iterate through each value of aboutArr.
Line 9: Each element in placeholderArr (which is presently empty) is created and set to the same value as each element in aboutArr. By the time the iteration is complete, placeholderArr contains the same elements as aboutArr in the same order.
Line 10: variable j is set to 0. j will be the counter used inside the inner function.
Line 12: placeholderArr[0] is displayed in browser.
Line 13: j is incremented by 1.
Line 14: loop is paused.
Per line 8, the loop is iterating until it reaches the last item in aboutArr. Because placeholderArr mirrors aboutArr, j will also stop incrementing once it reaches the last item in placeholderArr. The inner function will pause the iteration over placeholderArr, thereby incrementally pausing what is displayed by the browser.
- PM Career Story - April 28, 2022
- How to Transition into Product Management - December 26, 2017
- What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015
Comments