Evgeni
Evgeni

Reputation: 3343

Passing function with parameters as a parameter to a jQuery's callback

I'm having some difficulties with passing functions around. I have this code:

  var options = [
        { name: "menu 1", func: function (element) { $(element).css("color", "Red"); } },
        { name: "menu 2", func: function (element) { alert("menu 2"); } },
        { name: "menu 3", func: function (element) { alert("menu 3"); } }
    ];

        // This basically creates a div with ul, and li element for each object in options, 
        // and attaches click event to each li that should fire provided func. 
        (function menuMaker(options) {
            var menuDiv = $("<div id='test' />");
            var menuUl = $("<ul>");
            menuDiv.append(menuUl);

            for (var i = 0; i < options.length; i++) {
                var li = $("<li>" + options[i].name + "</li>");
                // **li.click(function () { options[i].func(menuDiv); });**  
                //>> The line above gives "options[i] is undefined" error. Looks like a scoping issue.

                var userFunc = options[i].func;
                **li.click(function(){ userFunc(menuDiv);});**  
                //>> The line above always fires the last function (alert("menu 3"))

                menuUl.append(li);
            }
            $(document.body).append(menuDiv);
        })(options);

and I'm getting those errors I've commented. Any ideas what am I doing wrong ?

Thanks !

Upvotes: 0

Views: 112

Answers (4)

Brand
Brand

Reputation: 107

Remove the "options" argument from "(function menuMaker(options) { ". The options variable is already set outside of the function. By adding it as an argument the function looks for the argument of "options" and not the predefined variable of options.

Im not great at explaining stuff, but as for passing the function through the for loop and to each individual click event, create an empty function like below, that way it doesnt reference the last value used.

Tested! Works!

var options = [
    { name: "menu 1", func: function (element) { $(element).css("color", "Red"); } },
    { name: "menu 2", func: function (element) { alert("menu 2"); } },
    { name: "menu 3", func: function (element) { alert("menu 3"); } }
];

(function menuMaker() {
    var menuDiv = $('<div id="test" />'),
        menuUl  = $('<ul />').appendTo(menuDiv);

    for (var i=0; i<options.length; i++) {

        (function() {
            var func = options[i].func;
            $('<li>' + options[i].name + '</li>')
            .click(function(){ func(menuDiv); })
            .appendTo(menuUl);        
        })();

    }

    $(document.body).append(menuDiv);
})();

Upvotes: 1

a&#39;r
a&#39;r

Reputation: 36999

Try this, which iterates over the array adding <li> elements. The function to be called is assigned to the local 'func' variable and then used within the click handler function.

$.each(options, function() {
    var func = this.func;

    menuUl.append(
        $('<li>').text(this.name).click(function () {
            func(menuDiv);
        })
    );
});

Upvotes: 1

aorcsik
aorcsik

Reputation: 15552

The problem is indeed a scoping one. The for loop does not create a new scope, so you have to create one inside it like:

for (var i=0; i<10; i++) {
    (function(i) {
        // "i" will be local here, and be accessible 
        // form any function defined inside this closure
    })(i);
}

Upvotes: 1

jbabey
jbabey

Reputation: 46647

Classic example of misunderstanding closures. See how to fix this here.

Upvotes: 1

Related Questions