意外的JS / jQuery循环行为

时间:2011-11-22 15:35:10

标签: javascript jquery

我有以下循环。目标是在用户选择下拉列表中的某些选项时自动显示文本框。

出于某种原因,在以下代码中,循环将“赞助商”字段分配给关联数组“blurb”。我无法弄清楚为什么。我怎样才能做到这一点?

非常感谢。

function add_fields_on_change ()
{
    var map = {
        "sponsor"   :   Array("New Sponsor", "new_sponsor"),
        "blurb"     :   Array("New Blurb Suggestion", "new_blurb")
    };
    for (field in map)
    {
        alert($('.bound[name='+field+']').val()); //alerts as expected
        $('.bound[name='+field+']').change(function() {
            alert(map[field][0]); //alerts "New Blurb Suggestion" for both "sponsor" and "blurb" fields
            if ($(this).val() == map[field][0])
            {
                 $('.hidden[name='+map[field][1]+']').show();
            }
        });
    }
}

2 个答案:

答案 0 :(得分:4)

原因是你正在创建的事件处理程序(它是一个闭包)对field变量有一个持久引用,而不是副本从创建函数时开始。因此,创建的所有事件处理程序将看到field(分配给它的最后一个值)的相同值。

因为您已经在使用jQuery,所以最简单的解决方案可能就是使用$.each

function add_fields_on_change ()
{
    var map = {
        "sponsor"   :   Array("New Sponsor", "new_sponsor"),
        "blurb"     :   Array("New Blurb Suggestion", "new_blurb")
    };
    $.each(map, function(field, value) {
        alert($('.bound[name='+field+']').val()); //alerts as expected
        $('.bound[name='+field+']').change(function() {
            alert(map[field][0]); //alerts "New Blurb Suggestion" for both "sponsor" and "blurb" fields
            if ($(this).val() == map[field][0])
            {
                 $('.hidden[name='+map[field][2]+']').show();
            }
        });
    });
}

这是有效的,因为事件处理程序关闭了对你传递给each的迭代器函数的调用,因此它们每个都得到自己的field参数,它永远不会改变。

如果你想在没有使用$.each的情况下出于某种原因这样做,你会做这样的事情:

function add_fields_on_change ()
{
    var map = {
        "sponsor"   :   Array("New Sponsor", "new_sponsor"),
        "blurb"     :   Array("New Blurb Suggestion", "new_blurb")
    };
    var field;
    for (field in map)
    {
        alert($('.bound[name='+field+']').val()); //alerts as expected
        $('.bound[name='+field+']').change(createHandler(field));
    }

    function createHandler(f) {
        return function() {
            alert(map[f][0]); //alerts "New Blurb Suggestion" for both "sponsor" and "blurb" fields
            if ($(this).val() == map[f][0])
            {
                 $('.hidden[name='+map[f][3]+']').show();
            }
        };
    }
}

这是相同的原则,事件处理程序关闭对createHandler的调用并使用f参数,该参数对于每个调用是唯一的,并且我们从不为其分配新值。

请注意,我声明了field变量。您的原始代码中缺少该声明,这意味着您正在成为Horror of Implicit Globals的牺牲品。

更多阅读:

答案 1 :(得分:0)

您可以通过向字段添加自定义属性来实现此目的,从而避免出现关闭问题:

function add_fields_on_change ()
{
    var map = {
        "sponsor"   :   Array("New Sponsor", "new_sponsor"),
        "blurb"     :   Array("New Blurb Suggestion", "new_blurb")
    };
    for (field in map)
    {
        $('.bound[name='+field+']').attr('data-name', map[field][0]);
        $('.bound[name='+field+']').attr('data-hidden', map[field][1]);
        $('.bound[name='+field+']').change(function() {
            if ($(this).val() == this.attr('data-name'))
            {
                 $('.hidden[name='+this.attr('data-hidden')+']').show();
            }
        });
    }
}