【问题标题】:Event binding on dynamically created elements and passing parameters动态创建元素上的事件绑定和传递参数
【发布时间】:2012-02-05 03:45:16
【问题描述】:

我在为我的问题寻找解决方案时发现了类似的问题,但我不认为它完全符合我的要求。

使用 jQuery 的 .on() 将事件绑定到动态创建的元素上毫无疑问,但是我在尝试传递不同的参数来调用不同的函数时遇到了困难。在阅读https://developer.mozilla.org/en/JavaScript/Guide/Closures 之后,我想出了以下工作示例,但我想知道它是否是最好的方法,或者是否有更优雅的解决方案?

http://jsfiddle.net/V25Zc/

另外,我经常读到由于漏洞问题,使用 eval() 并不是最佳实践,但是否有另一种方法来构建动态函数名称?

编辑:

非常感谢大家的精彩回答!但是其中一个答案是否被删除了?我喜欢那个,因为提示将参数存储在属性中。再次感谢大家!

【问题讨论】:

    标签: javascript jquery events closures


    【解决方案1】:

    事件委托的要点是您不需要将事件绑定到每个元素(或多次绑定到同一个元素),只需将事件绑定到公共父级,如下所示:

    function createLinks() {
    
        var $container = $("#container"),
            links = "";
    
    
    
        for (var i=0; i < 4; i++) {
            links += '<a class="linklink" href="#">Link_' + i + '</a>';
        }
    
        $container.html(links).on( "click", ".linklink", function(){
            console.log( "fn action"+$(this).index());
        });
    }
    
    createLinks();
    

    为避免使用eval,您可以拥有一个函数数组并按索引调用它们:

    arrayOfFunctions[ $(this).index() ]();
    

    http://jsfiddle.net/V25Zc/2/

    【讨论】:

    • +1 - 这是正确的答案。我会抛出一个 data-foo 属性来存储 i,但无论如何
    • @AdamRackis 是的,我这样做是出于纯粹的懒惰
    • @Esailija 非常感谢这个解决方案,因为这样,循环是完全干净的(内部没有函数)。另外,甚至不需要辅助函数! :)
    【解决方案2】:

    如果每个动作函数实际上必须是一个不同的函数(而不是一个根据传递给它的索引而行为不同的函数),那么我会通过在链接上放置一个属性并获取它来做到这一点点击你可以在这里看到它的工作:http://jsfiddle.net/jfriend00/gFmvG/

    function action0(){ console.log("fn action0"); }
    function action1(){ console.log("fn action1"); }
    function action2(){ console.log("fn action2"); }
    function action3(){ console.log("fn action3"); }
    
    var actions = [action0, action1, action2, action3];
    
    function createLinks() {
    
        var $container = $("#container"),
            links = "";
    
        for (var i=0; i < 4; i++) {
            links += '<a id="link_' + i + '" href="#" data-num="' + i + '">Link_' + i + '</a>';
            $container.on("click", '"#link_' + i + '"', function() {
                actions[$(this).data("num")]();
            });
        }
    
        $container.html(links);
    }
    
    createLinks();
    

    如果您不必为每个操作设置单独的函数,我会这样做,您可以在此处看到:http://jsfiddle.net/jfriend00/Z8Rq6/

    function doAction(index) {
        console.log("fn action" + index);
    }
    
    function createLinks() {
    
        var $container = $("#container"),
            links = "";
    
        for (var i=0; i < 4; i++) {
            links += '<a id="link_' + i + '" href="#" data-num="' + i + '">Link_' + i + '</a>';
            $container.on("click", '"#link_' + i + '"', function() {
                doAction($(this).data("num"));
            });
        }
    
        $container.html(links);
    }
    
    createLinks();
    

    这也可以通过锁定索引值的执行闭包来完成,但我发现语法的可读性稍差(阅读代码并知道它在做什么需要太多的大脑周期)所以我更喜欢这种方式属性。

    【讨论】:

    • 按照承诺+1。你能检查一下我的回答,以确保我没有说什么else愚蠢的话吗?
    • @jfriend00 我赞成这个,因为我真的很喜欢获取属性的想法。太糟糕了,不能同时接受两个答案。 :(
    【解决方案3】:

    把你的函数放在一个对象中

    var myFuncs = {
    action0:function(){ console.log("fn action0"); },
    action1:function(){ console.log("fn action1"); },
    action2:function(){ console.log("fn action2"); },
    action3:function(){ console.log("fn action3"); }
    };
    
    var funcNumber = "3";
    
    for (var i=0; i < 4; i++) 
    {
        links += '<a id="link_' + i + '" href="#">Link_' + i + '</a>';
        (function(myI)
        {
             $container.on("click", '"#link_' + i + '"', function()
             {
                  myfuncs["action"+funcNumber](mI);
             });
        })(i);
    }
    

    当然,如果它们是在全局范围内制作的,你也可以这样做

    for (var i=0; i < 4; i++) 
    {
        links += '<a id="link_' + i + '" href="#">Link_' + i + '</a>';
        (function(myI)
        {
             $container.on("click", '"#link_' + i + '"', function()
             {
                  window["action"+funcNumber](mI);
             });
        })(i);
    }
    

    【讨论】:

    • 非常感谢,这绝对是避免 eval() 的好方法。 :)
    • 添加了一个匿名闭包,如果您要在循环中使用它,以便您在回调中获得正确的“i”值
    【解决方案4】:

    看起来你最初的问题是你想调用一个函数,它的名字将由传入的参数决定。这些函数在范围内都是全局的吗?如果是这样,我会这样做:

    function helpCallback(index) {
        return function() {
            window['action' + index]();
        }
    }
    

    这有一个额外的好处,如果您想将参数传递给 actionX,您可以通过在索引后传递这些参数并进行以下修改来实现

    function helpCallback(index) {
        var args = arguments;
        return function() {
            window['action' + index].apply(null, [].slice.call(args, 1));
        }
    }
    

    所以helpCallback(1, "foo"); 会返回一个调用action1('foo') 的函数

    【讨论】:

    • 这不起作用,因为i 的值将是 4(for 循环的结尾),而不是实际执行 helpCallback(i) 时想要的值。这是人们在处理将来会被调用的函数时经常陷入的陷阱。这类问题通常通过创建一个新的闭包来解决,在执行 for 循环时将 i 传递给该闭包。
    • @jfriend00 - 你是否在我回答错误的 5 秒内写了该评论?我最初误解了发生了什么,但我认为我目前的答案是正确的。
    • 问题是helpCallback() 返回一个函数,所以像你正在做的那样把它放到一个匿名函数中并不能解决任何问题。我认为您误解了 OP 对 helpCallback() 所做的事情。
    • @jfriend00 - 我明白了,但是 OP 的原始代码不起作用吗? $container.on("click", '"#link_' + i + '"', helpCallback(i)); 不立即执行 helpCallback(i) 吗?
    • 他们原来的方法确实有效。阅读问题 - 他们在询问是否有更好的方法。
    【解决方案5】:

    我个人不会费心创建 ID 然后引用它们。为什么不在绑定到您创建的实际元素中使用闭包。

    例如,你可以做类似的事情

    function createLinks() {
    
        var $container = $("#container");
    
        for (var i=0; i < 4; i++) {
            var link = $('<a href="#link' + i + '">Link_' + i + '</a>');
            $container.append(link);
            (function(){
                var index = i;
                link.click(function(){
                    alert("Do my thing " + index);
                });
            })();
        }
    
    }
    
    createLinks();
    

    【讨论】:

    • 我认为这行不通。 alert() 将始终将 i 显示为 4(for 循环末尾的值)。
    • 该死的,jfriend00。我错过了。现在已修复,使用匿名函数闭包。
    猜你喜欢
    • 2022-11-22
    相关资源
    最近更新 更多