Alternative way to using eval( ) to creating a dynamic variable within a for( ) while running jQuery?
I'm fairly new to JS and not very familiar with jQuery, but I'd like to give a shot at dynamically calling slimbox's multi-image function.
For example, there is a photoslider that randomly cycles through 3 images on my website, and I would like slimbox to show 2 more images for the first and 1 more for the last. This means slimbox will display a total of 6 images in the slideshow.
So in case you need it, here's the detailed walk through: when the user visits my site, the user will see a div block that shows 1 image at a time. The image in the photoslider will rotate out to the next one (of the 3 randomly loaded). When the user clicks on the image, the user will trigger the slimbox (lightweight lightbox) and a larger slideshow will take up the screen (dimming the background). Through slimbox, the user will see a up to a total of 6 images cycle through.
In order to create a dynamic variable, I tried to use EVAL within a FOR loop, but this fails when running jQuery. EVAL works outside of the loop, though. I'm not sure which other function to use instead. Let's dig into the code...
Calling slimbox via JS:
// usage as shown for multi-image slideshow. yes, there are arrays within an array
jQuery.slimbox([["image","desc"], ["image","desc"], ["image","desc"]], startAtImage);
// or simplified further
jQuery.slimbox([[array1], [array3], [array2]], startAtImage);
// after appending the images to select the first and last images
jQuery.slimbox([[array1], [array4], [array5], [array3], [array2], [array6]], startAtImage);
Here's my attempt at dynamically calling the slimbox function:
var imgAlbum = "summer";
var arrayGiven = [1,3,2]; // randomized image order given
var img1 = [4,5]; // registering images to be appended -- aiming for [1,4,5]
var img2 = [6]; // registering images to be appended -- aiming for [2,6]
var desc1 = "Description #1"; // unique descriptions
var desc2 = "Description #2";
var desc3 = "Description #3";
var desc4 = "Description #4";
var desc5 = "Description #5";
var desc6 = "Description #6";
var arrayExtended = arrayGiven;
for(var i=1; i<=3; i++){ // cycle through arrayGiven and append images
var imgLocate = i;
var imgAtIndex = jQuery.inArray(imgLocate,arrayExtended); // locating array index number of a specific image. if imgLocate = 1, then imgAtIndex = 0 , if 2 then 4, and so forth
var imgAfter = imgAtIndex + 1; // allows appending to selected image at index
eval("var imgCurrent = img" + imgLocate + ";"); // creating dynamic variable
if(imgCurrent != null){
arrayExtended.splice(imgAfter, 0, imgCurrent);
}
} // alert(arrayExtended) should be [1,4,5,3,2,6]
for(var i=1; i<=arrayExtended.length; i++){
var imgLocate = i;
eval("var array" + imgLocate + " = ['img/" + imgAlbum + "/" + imgLocate + ".jpg',desc" + imgLocate + "];"); // if imgLocate = 1, then var array1 = ['img/summer/1.jpg',desc1];
// array1 array2 array3 array4 array5 array 6 ... now defined
}
var arrayMain = [];
for(var i=1; i<=arrayExtended.length; i++){ // must wait for arrays to be built before adding to arrayMain
var atIndex = i - 1;
var imgAtIndex = jQuery.inArray(imgLocate,arrayExtended); // if imgLocate = 1, then imgAtIndex = 0 , if 2 then 4
eval("arrayMain.push('[' + array" + arrayExtended[atIndex] + " + ']');");
}
var imgClicked = 0; // clicked on image within arrayExtended index 0
var startAtImage = imgClicked; // slimbox slideshow will start on 1.jpg
jQuery.slimbox("[" + mainArray + "]",startAtImage);
And finally hoping for the following result:
jQuery.slimbox([["img/summer/1.jpg","Desciption #1"], ["img/summer/4.jpg","Desciption #4"], ["img/summer/5.jpg","Desciption #5"], ["img/summer/3.jpg","Desciption #3"], ["img/summer/2.jpg","Desciption #2"]], ["img/summer/6.jpg","Desciption #6"], 0);
Any help or advice will be much appreciated! There's much to learn.
Updated 8-26-11 Changed structure based on advices. Also added more images and groups into array to better illustrate the functionality needed. There are errors when trying to call image groups (1, 2, 7) from img object:
var imgAlbum = "summer";
var arrayGiven = [1,3,2,7,11]; // randomized image order given
var img = {
1: [4,5],
2: [6],
7: [10,8,9]
};
var desc = {
1: 'Description #1',
2: 'Description #2',
3: 'Description #3',
4: 'Description #4',
5: 'Description #5',
6: 'Description #6',
7: 'Description #7',
8: 'Description #8',
9: 'Description #9',
10:'Description #10',
11:'Description #11'
};
var arrayExtended = arrayGiven.slice();
for(var i=1; i<=arrayGiven.length; i++){
var imgLocate = i;
var imgAtIndex = jQuery.inArray(imgLocate,arrayExtended);
var imgAfter = imgAtIndex + 1;
var imgCurrent = img[imgAfter];
if(imgCurrent != null){
arrayExtended.splice(imgAfter, 0, imgCurrent);
}
//alert(imgCurrent); // shows 4,5 undefined undefined undefined undefined
} //alert(arrayExtended);// shows 1,4,5,3,2,6,7,10,8,9,11
var imgArray = {};
for(var i=1; i<=arrayExtended.length; i++){
var imgLocate = i;
var atIndex = i - 1;
imgArray[imgLocate] = ['imgs/'+imgAlbum+'/'+imgLocate+'.jpg', desc[imgLocate]];
}
var mainArray = [];
for(var i=1; i<=arrayExtended.length; i++){
var imgLocate = i;
var atIndex = i - 1;
mainArray.push(imgArray[arrayExtended[atIndex]]);
//alert(imgArray[arrayExtended[atIndex]]); // shows (simplified) 1.jpg,Desc#1 undefined 3.jpg,Desc#3 2.jpg,Desc#2 undefined undefined
}
var imgClicked = 0;
var startAtImage = imgClicked;
jQuery.slimbox(mainArray,startAtImage);
Updated 8-30-11 FINAL SOLUTION Fixes applied and added randomizer from production code. The results are accurate. If you plan on using the randomizer, I recommend keeping the images-to-be-appended outside of the getRandomArray range, unless you wish to see duplicates.
Thanks everyone for your awesome support!
function getRandomArray(min,max){
var A= [];
while(max>= min) A.push(max--)
A.sort(function(){return .5- Math.random()});
return A;
}
var randomness = getRandomArray(1,11).slice();
var leadingZeroArray = randomness.slice();
leadingZeroArray.unshift("0"); // was needed for my project, but if removed, everything else will need to be re-adjusted
var arrayGiven = randomness.slice(); // or you can plug in -> var arrayGiven = [1,3,2,7,11]; for original example
var imgAlbum = "summer";
var img = {
1: [4,5],
2: [6],
7: [10,8,9]
};
var desc = {
1: 'Description #1',
2: 'Description #2',
3: 'Description #3',
4: 'Description #4',
5: 'Description #5',
6: 'Description #6',
7: 'Description #7',
8: 'Description #8',
9: 'Description #9',
10:'Description #10',
11:'Description #11'
};
var arrayExtended = arrayGiven.slice();
for(var i=0; i<arrayGiven.length; i++){
var imgLocate = arrayGiven[i];
var imgAtIndex = jQuery.inArray(imgLocate,arrayExtended);
var imgAfter = imgAtIndex + 1;
开发者_StackOverflow社区 var imgCurrent = img[imgLocate];
if(imgCurrent != null){
if(imgCurrent.length > 0){
for(var j=imgCurrent.length-1; j>-1; j--){
var imgMore = imgCurrent[j];
arrayExtended.splice(imgAfter, 0, imgMore);
}
}
else{arrayExtended.splice(imgAfter, 0, imgCurrent); }
}
}
var imgArray = {};
for(var i=1; i<=arrayExtended.length; i++){
var imgLocate = i;
var atIndex = i - 1;
imgArray[imgLocate] = ['images/'+imgAlbum+'/'+imgLocate+'.jpg', desc[imgLocate]];
}
var mainArray = [];
for(var i=1; i<=arrayExtended.length; i++){
var imgLocate = i;
var atIndex = i - 1;
mainArray.push(imgArray[arrayExtended[atIndex]]);
}
var imgClickedOn = 5;
var imgAtIndex = jQuery.inArray(imgClickedOn,arrayExtended);
jQuery.slimbox(mainArray,imgAtIndex);
Instead of having multiple vars for img
, use an array or an object.
var img = {
1: [4,5],
2: [6]
};
Then instead of eval("var imgCurrent = img" + imgLocate + ";");
, you can do:
var imgCurrent = img[imgLocate];
You can do the same sort of thing for:
eval("var array" + imgLocate + " = ['img/" + imgAlbum + "/" + imgLocate + ".jpg',desc" + imgLocate + "];");
Create an object and put the arrays in there:
var desc = {
1: 'Description #1',
2: 'Description #2',
3: 'Description #3',
4: 'Description #4',
5: 'Description #5',
6: 'Description #6'
};
var imgArray = {};
imgArray[imglocate] = ['img/'+imgAlbum+'/'+imgLocate+'.jpg', desc[imgLocate]];
EDIT: Try changing var imgLocate = i;
inside for(var i=1; i<=arrayGiven.length; i++){
to var imgLocate = arrayGiven[i];
.
The global variables are members of the window
object, so you can just do:
var imgCurrent = window["img" + imgLocate];
However, using dynamic variable names like that is not a good practice. You should just put the values in an array:
var img = [
[4,5],
[6]
];
Now you can just access the array items:
var imgCurrent = img[imgLocate];
Note however that the array indices start at 0, not 1.
A few quick notes:
Please don’t use
eval()
. There are very, very few cases where it’s needed, and it has performance and security implications.You’ve discovered that JavaScript doesn’t have “variable variables” (the ability to get or set a variable by name), which is why you had to resort to
eval()
. When you need to look something up out of a set, don’t use a bunch of numbered variables. Try an array or an object instead.Objects in JavaScript don’t get copied when you assign them to variables. The line
var arrayExtended = arrayGiven;
doesn't make a copy of
arrayGiven
—arrayExtended
ends up being a variable which points at the same array. So, when you change something inarrayExtended
, it also changes it inarrayGiven
. If you need to copy an array in JavaScript, use.slice()
with no arguments:var arrayExtended = arrayGiven.slice();
Some other things in JavaScript, like objects, are trickier to copy.
What are you trying to do here? (
mainArray
is already an array) This is going to create a string which is a “[”, followed by the string representation ofmainArray
(each item in the array separated by commas), followed by “]”. Slimbox won’t know what to do with it.jQuery.slimbox("[" + mainArray + "]",startAtImage);
If you do want to wrap something in an array, just put brackets around it like you did up above:
[mainArray];
I think your main problem is "[" + mainArray + "]"
. With those tips, you should be able to make your code work (and, without eval()
). But, I want to show you my approach to this problem too. It’s below.
I’m storing the names and descriptions of all the images in an array. Because arrays in JavaScript (and a lot of other programming languages) start at 0 instead of one, I subtracted one from all the image numbers in the arrays that specify the order.
var album = "summer",
images = [
[
{ name: "1", description: "Description #1" },
{ name: "4", description: "Description #4" },
{ name: "5", description: "Description #5" }
],
[
{ name: "2", description: "Description #2" },
{ name: "6", description: "Description #6" }
],
[
{ name: "3", description: "Description #3" }
]
],
order = [0, 2, 1],
startAtImage = 0,
slimboxImages = Array.prototype.concat.apply([], order.map(function(id){ return images[id]; }))
.map(function(image){
return [ 'img/' + album + '/' + image.name + '.jpg', image.description];
});
jQuery.slimbox(slimboxImages, startAtImage);
any reason you can't just do
var imgCurrent = 'img' + imgLocate;
? It is basically NEVER a good idea to dynamically generate/execute code from within a script. It's next to impossible to debug, and you're not even really doing anything that can't be done WITHOUT eval in the first place.
精彩评论