0

I'm using Laravel Elixir and Laravel-Elixir-SpriteSmith to create some sprite icons. I have at least 3 different icon sizes stored on different folders, so I tried to iterate over these folders and execute the task to generate sprites for each of them:

My problem is that in cssOpts.cssSelector function of the second argument passed to mix.spritesmith the value of setis always the last value.

The code explains it better:


var sources = [
        { size: 16, source: 'resources/bower_components/famfamfam-silk/dist/png' },
        { size: 22, source: 'resources/assets/icons/22' },
        { size: 38, source: 'resources/assets/icons/38' }
    ];

    for(var i in sources) {
        var set = sources[i];

        mix.spritesmith(set.source, {
            imgOutput: 'public/icons',
            cssOutput: 'public/css',
            imgName: set.size + '.png',
            cssName: 'icons-' + set.size + '.css',
            imgPath: '../../icons/' + set.size + '.png',
            cssOpts: {
                cssSelector: function(item) {
                    console.log(set.size); //PROBLEM HERE. Always prints 38! Should print 16 - 22 - 38.
                    return '.icon' + set.size + '-' + item.name.replace('_', '-');
                }
            }
        });
    }

What should I do to pass the right value to the function at each loop?

Rodrigo Balest
  • 326
  • 10
  • 17

2 Answers2

2

The problem you're experiencing is called late binding. You can read about it on multiple SO questions, for example this: Late binding onclick event

You basically need to make a new scope, which will have a certain value of set bound to it.

cssSelector: (function(innerSet) {
    return function(item) {
        console.log(innerSet.size);
        return '.icon' + innerSet.size + '-' + item.name.replace('_', '-');
    };
})(set)
Community
  • 1
  • 1
ralh
  • 2,514
  • 1
  • 13
  • 19
1

I agree with the others, this is due to how closures and variable hoisting work in javascript : var set is part of the upper function scope, not the loop. So by the time cssSelector is executed, set is equal to the last element in the array.

You could use @ralh's way, or use the forEach method :

var sources = [{
    size: 16,
    source: 'resources/bower_components/famfamfam-silk/dist/png'
}, {
    size: 22,
    source: 'resources/assets/icons/22'
}, {
    size: 38,
    source: 'resources/assets/icons/38'
}];

sources.forEach(function (set) {
    mix.spritesmith(set.source, {
        imgOutput: 'public/icons',
        cssOutput: 'public/css',
        imgName: set.size + '.png',
        cssName: 'icons-' + set.size + '.css',
        imgPath: '../../icons/' + set.size + '.png',
        cssOpts: {
            cssSelector: function(item) {
                console.log(set.size); //PROBLEM HERE. Always prints 38! Should print 16 - 22 - 38.
                return '.icon' + set.size + '-' + item.name.replace('_', '-');
            }
        }
    });
})
kombucha
  • 1,424
  • 1
  • 11
  • 19