1

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;                                  
            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);
Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
Transattic
  • 40
  • 1
  • 7
  • I think there might be a simpler way to do this, but I'm having some trouble understanding the *intent* of code. Do you think you could edit the question a little bit to describe what you want the code to do, in the end? – s4y Aug 24 '11 at 19:43
  • I added more info below the 2nd paragraph. – Transattic Aug 25 '11 at 00:56
  • I’m actually wondering about your code, the 2nd paragraph. Do you mean that you'd like to pass a total of six images to Slimbox, the first, fourth, and fifth being random, but want two specific images to be second and third and another to be last? Or, do you mean that you want to display three images in random order, with two images after a specific one (image #1) and another after a different specific one (image #2), no matter where they are in the random arrangement? Or, do you mean something else? – s4y Aug 25 '11 at 01:07
  • It's a bit late to answer, but might as well... the page will show 3 random images in random order. When the user clicks on an image and that particular image has additional images linked to it, then slimbox will display that particular image clicked on plus X linked images to follow after. Image links (1.jpg -> 4.jpg, 5.jpg) (2.jpg -> 6.jpg) will be added into the array. So if on the home page, we see this order of images in the photoslider: 3.jpg, 1.jpg, 2.jpg, when clicked on 2.jpg slimbox will show 2.jpg, 6.jpg, 1.jpg, 4.jpg, 5.jpg, 3.jpg. Does that clear things up? – Transattic Aug 25 '11 at 23:32

4 Answers4

4

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];.

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • Hey Rocket, this so far has been a the best solution. Though, I am running into walls like passing the array values from the object. Here's what the code looks like now: `jQuery.slimbox(mainArray,startAtImage);` – Transattic Aug 25 '11 at 23:03
  • CORRECTION Though, I am running into walls like passing the array values from the object. Here's what the code looks like now after also applying your fix and Sidnicious's bracket fix: `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 jQuery.slimbox(mainArray,startAtImage);` 4,5 is one and 6 another, but they aren't the same kind of values as 1,3,2. I can update my original post to show my entire work if you want. – Transattic Aug 25 '11 at 23:13
  • @Transattic: Can you update the original post? This will make it easier for others to help too. – gen_Eric Aug 26 '11 at 13:25
  • Thanks for the solution. Everything works now after a bit more debugging. Update posted. – Transattic Aug 30 '11 at 06:56
2

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.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
1

A few quick notes:

  1. Please don’t use eval(). There are very, very few cases where it’s needed, and it has performance and security implications.

  2. 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.

  3. Objects in JavaScript don’t get copied when you assign them to variables. The line

    var arrayExtended = arrayGiven;
    

    doesn't make a copy of arrayGivenarrayExtended ends up being a variable which points at the same array. So, when you change something in arrayExtended, it also changes it in arrayGiven. 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.

  4. 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 of mainArray (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);
s4y
  • 50,525
  • 12
  • 70
  • 98
  • 1. to 3. Good to know these things, thanks. 4. If I am adding a value to the middle of an array (w/o changing a value), will that method work instead of splice? 5. I noticed my code wasn't working because the brackets were strings. This helped fix the input, thanks for the insight. I'm going to read the rest of your code when I get back. – Transattic Aug 25 '11 at 23:22
  • Hey Sidnicious, I just ran your proposed version and see that it has issues dealing with randomized orders. Yes it works great for order [0,2,1] (or imgs 1,3,2 to avoid confusion with the original question), but when inputting order [2,1,0] (imgs 3,2,1), slimbox shows in this order: 3,4,5,2,1,6. We should expect this outcome: 3,2,6,1,4,5. Any thoughts on how to better take in random orders? – Transattic Aug 26 '11 at 01:31
  • @Transattic I misunderstood what you were trying to do. Essentially, you want to group some images together. Some of the images are randomized, but others always show up after specific images. I edited my answer, take a look. – s4y Aug 26 '11 at 01:58
  • this works well for these 3 image groups. I guess I didn't mention that this should work for a large set of images up to 50 or more. So there will be many points where the image clicked on will not be the first in that particular group. I just ran tests by adding groups, like so: image array index #0 (1,4,5) #1 (2,6) #2 (3) #3 (7,10,8,9) #4 (11). When the user clicks on (startAtImage) index #10 (img 11), slimbox instead shows img 9. And if clicked index #6 (img 7), shows img 11. I'm not sure how this works, but would it be possible to always start with the img clicked on? – Transattic Aug 26 '11 at 07:17
  • @Transattic I'm guessing that `startAtImage` doesn't account for the extra images (an image might be in one position in the `order` array, but in another position as far as Slimbox is concerned, if extra (grouped) images come before it. – s4y Aug 26 '11 at 16:54
  • @Transattic I think that the other answers here should help you move in the right direction, try taking the next step yourself! Myself and the others on Stack Overflow are always happy to help with specific problems and questions that you run into while programming, but making your code do what you want it to do, overall, is your job. – s4y Aug 26 '11 at 16:58
  • Thanks for your involved efforts! Now I learned another way. – Transattic Aug 26 '11 at 17:27
  • P.S. You're right — you do need `splice` to add something to the middle of an array. – s4y Aug 26 '11 at 18:56
0

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.

Marc B
  • 356,200
  • 43
  • 426
  • 500