简体   繁体   中英

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 !

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

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);
}

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);
        })
    );
});

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);
})();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM