为什么使用val ()设置select的值时,jQuery更改事件不会触发?

403

当使用val()设置值时,change()事件处理程序中的逻辑不会运行,但是当用户使用鼠标选择值时,它会运行。为什么会这样?

<select id="single">
    <option>Single</option>
    <option>Single2</option>
</select>

<script>
    $(function() {
        $(":input#single").change(function() {
           /* Logic here does not execute when val() is used */
        });
    });

    $("#single").val("Single2");
</script>
10个回答

632

因为change事件需要由用户实际发起的浏览器事件,而不是通过JavaScript代码。

请改用以下方法:

$("#single").val("Single2").trigger('change');
或者
$("#single").val("Single2").change();

1
嗨,我正在更新下拉菜单。但是在调用onChange()时出现了递归调用的问题。 - Pankaj
3
我理解你的意思。这个答案不错,但是有没有更好的jQuery方法呢?我相信我们都会记得在每次改变设置了change处理程序的值时执行此操作(讽刺)。使用数据绑定的框架可能能更好地解决这个问题。 - Jess
@user113716,您是否知道如何在不知道任何值的情况下触发它? - BenKoshy
4
我同意这是正确的答案,但这绝对糟糕,会破坏我的尝试。 - Dustin Poissant
尝试使用以下代码:$("#single").trigger({type: "change", val: "Single2"}) - user2901633

48

我相信你可以使用trigger()手动触发更改事件:

$("#single").val("Single2").trigger('change');

虽然为什么它不会自动触发,我不知道。


我无法让它工作,即使是$("#single").val("Single2").change();我正在动态创建下拉菜单并更改其值(对我来说运行良好)但是当我尝试触发更改时,它对我不起作用。 - Maneesh Kumar
5
有点晚了,但它不会自动执行,因为这会导致无限循环。想想 $('#foo').change(function() { $( this ).val( 'whatever' ); }); - JJJ
@Juhana 我认为无限循环比val()神秘地未能触发“change”绑定更容易注意到,诊断和修复。 - iono
调用 .change() 是调用 .trigger('change') 的简写。 - Triynko

10
在使用 val() 后添加这段代码似乎可以解决问题:
$(":input#single").trigger('change');

6
是的,但你不需要使用 $(":input#single") 进行单独的DOM选择。事实上,你真的不应该这样做。只需在 .val() 后面链接它即可。你可以使用 .trigger('change').change()。它们完全相同。 - user113716

8
据我所阅读的API,只有当用户点击选项时才会触发事件。
对于选择框、复选框和单选按钮,当用户使用鼠标进行选择时,事件会立即触发;但对于其他元素类型,事件会延迟到元素失去焦点时触发。
参考链接:http://api.jquery.com/change/

其他元素类型有哪些替代方案?我希望对于“input”元素,“change”事件立即触发,而不是在元素失去焦点时触发。你知道吗? - Dan Nissenbaum
1
如果你的“立即”是指“当输入中键入任何键时”,那么你应该使用onkeyup、onkeypress和onkeydown事件,而不是onchange。 - user2867288

7
为了使操作更简单,可以添加自定义函数,并在需要更改值并触发更改时调用它:
$.fn.valAndTrigger = function (element) {
    return $(this).val(element).trigger('change');
}

并且

$("#sample").valAndTrigger("NewValue");

或者你可以重写 val() 函数,使其在每次调用 val() 时都调用 change 函数:

(function ($) {
    var originalVal = $.fn.val;
    $.fn.val = function (value) {
        this.trigger("change");
        return originalVal.call(this, value);
    };
})(jQuery);

样例请访问http://jsfiddle.net/r60bfkub/


我的浏览器报告递归过多,有什么解决方法吗? - Bhargav Nanekalva
你正在监听控件上的change事件,并执行一个回调函数,该函数(直接或间接地)再次触发相同控件上的change事件。我建议你为手动触发的事件使用另一个名称(例如explicit.change),以便它不会重新触发change事件,避免循环,并仍然允许你对事件做出反应。 - Kamafeather

3

我在使用CMB2和WordPress时遇到了同样的问题,想要钩入文件上传元框的更改事件。

所以,如果您无法修改调用更改的代码(在这种情况下是CMB2脚本),请使用以下代码。 触发器在值设置之后被调用,否则您的更改事件处理程序将起作用,但值将是先前的值,而不是正在设置的值。

以下是我使用的代码:

(function ($) {
    var originalVal = $.fn.val;
    $.fn.val = function (value) {
        if (arguments.length >= 1) {
            // setter invoked, do processing
            return originalVal.call(this, value).trigger('change');
        }
        //getter invoked do processing
        return originalVal.call(this);
    };
})(jQuery);

这很好,但你能展示如何将其限制为仅适用于特定输入吗? - jwebb
@jwebb 不太确定你的意思?我们正在覆盖jQuery val()函数。您将更改事件处理程序绑定到特定输入。 - user2075039
我的意思是,@user2075039,如果页面上使用jQuery val()功能有多个输入,并且您不想将自定义更改事件处理程序绑定到所有这些输入中的每一个,而只是其中一个,怎么办? - jwebb
@jwebb 您可以在覆盖的 $ .fn.val 函数内检查 this.selector,以获取特定选择器(将取决于所用于选择器的内容)。这是我发现的仅针对特定字段执行此操作的最佳方法,唯一的问题是您必须知道他们使用的选择器是什么。 - sMyles

3

如果您不想与默认更改事件混淆,可以提供自定义事件

$('input.test').on('value_changed', function(e){
    console.log('value changed to '+$(this).val());
});

要触发值设置上的事件,您可以执行以下操作:
$('input.test').val('I am a new value').trigger('value_changed');

3

如果您刚刚向表单添加了选择选项,并希望触发更改事件,我发现需要使用setTimeout,否则jQuery无法检测到新添加的选择框:

window.setTimeout(function() { jQuery('.languagedisplay').change();}, 1);

1
$(":input#single").trigger('change');

这对我的脚本起作用。我有3个组合框并绑定了chainSelect事件,我需要通过url传递3个值并默认选择所有下拉菜单。我使用了这个

$('#machineMake').val('<?php echo $_GET['headMake']; ?>').trigger('change');

第一个事件已经成功执行。


10
希望你在现实生活中永远不要这样做。毋庸置疑,$_GET['whatever'] 不能被信任。 - Denis V

0

改变数值

$("#single").val("Single2");

同时触发一个变更事件

$("#single").val("Single2").change();

当页面上有多个选择选项时,这种逻辑非常重要。
一个选项更改后,其他选择选项也必须更改,但不会触发更改事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接