Почему событие изменения jquery не срабатывает, когда я устанавливаю значение выбора с помощью val ()?

377

Логика в change()обработчике событий не запускается, когда значение установлено val(), но она запускается, когда пользователь выбирает значение с помощью мыши. Почему это?

<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>
Эрик Белаир
источник

Ответы:

589

Поскольку changeсобытие требует фактического события браузера, инициированного пользователем, а не через код JavaScript.

Сделайте это вместо этого:

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

или

$("#single").val("Single2").change();
user113716
источник
1
Привет, делаю это обновление выпадающего. но вызывается onChange () рекурсивно.
Панкадж
3
Я копаю этот ответ. Это работает, но разве нет лучшего способа с jQuery? Я уверен, что мы ВСЕ будем помнить делать это каждый раз, когда меняем значение, для которого настроен обработчик изменений (сарказм). Фреймворки, использующие привязку данных, могут решить эту проблему лучше?
Джесс
@ user113716 ты бы знал, как это сделать, не зная значений?
BKSpurgeon
4
Я согласен, что это правильный ответ, но это абсолютно отстой и разрушает то, что я пытаюсь сделать.
Дастин Пуассан
Попробуйте $ ("# single"). Trigger ({type: "change", val: "Single2"})
user2901633
44

Я считаю, что вы можете вручную вызвать событие изменения с помощью trigger():

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

Хотя почему он не срабатывает автоматически, я понятия не имею.

Дэвид говорит восстановить Монику
источник
Я не могу заставить это работать, также $ ("# single"). Val ("Single2"). Change (); Я динамически создаю выпадающий список и меняю его значение (у меня это нормально работает) Но когда я пытаюсь вызвать изменения, у меня это не работает.
Маниш Кумар
4
«Бит» запоздал, но он не срабатывает автоматически, потому что это приведет к бесконечным циклам. Подумайте$('#foo').change(function() { $( this ).val( 'whatever' ); });
JJJ
@Juhana Я думаю, что бесконечный цикл гораздо легче заметить, диагностировать и исправить, чем val (), который таинственным образом не запускает привязки «изменения».
Ионо
Вызов .change () является сокращением для вызова .trigger ('change').
Трийнко
9

Добавление этого фрагмента кода после val (), кажется, работает:

$(":input#single").trigger('change');
Эрик Белаир
источник
6
Да, но вам не нужно делать для этого отдельный выбор DOM $(":input#single"). На самом деле вы действительно не должны. Просто зацепи это после .val(). Вы можете использовать .trigger('change')или .change(). Они точно такие же.
user113716
8

Насколько я могу прочитать в API. Событие запускается только тогда, когда пользователь нажимает на опцию.

http://api.jquery.com/change/

Для полей выбора, флажков и переключателей событие вызывается сразу же, когда пользователь делает выбор с помощью мыши, но для других типов элементов событие откладывается до тех пор, пока элемент не потеряет фокус.

Marnix
источник
Каковы альтернативы для других типов элементов? Я хочу, чтобы событие «change» запускалось сразу для inputэлементов, а не тогда, когда элемент теряет фокус. Ты знаешь?
Дан Ниссенбаум
1
Если под «немедленно» вы подразумеваете «всякий раз, когда нажимается клавиша на входе», вы ищете события onkeyup, onkeypress и onkeydown, а не onchange.
user2867288
6

Для упрощения добавьте пользовательскую функцию и вызывайте ее, когда захотите, чтобы изменение значения также вызывало изменение

$.fn.valAndTrigger = function (element) {
    return $(this).val(element).trigger('change');
}

а также

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

Или вы можете переопределить функцию val, чтобы всегда вызывать изменение при вызове val

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

Образец на http://jsfiddle.net/r60bfkub/

Алиреза Фаттахи
источник
Мой браузер жалуется на слишком много рекурсии, есть какое-то решение для этого?
Бхаргав Нанекалва
Вы прослушиваете change-event для элемента управления и выполняете обратный вызов, который (прямо или косвенно) снова запускает change-event для того же элемента управления. Я предлагаю вам использовать другое имя для события, которое вы запускаете вручную (например explicit.change), чтобы оно не повторно changeзапускало -event, предотвращало цикл и все же позволяло вам реагировать на событие.
Kamafeather
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');
codingrhythm
источник
3

Я столкнулся с той же проблемой при использовании CMB2 с Wordpress и хотел подключиться к событию изменения метабокса загрузки файла.

Поэтому, если вы не можете изменить код, который вызывает изменение (в данном случае сценарий CMB2), используйте код ниже. Триггер вызывается ПОСЛЕ установки значения, в противном случае ваш EventHandler изменения будет работать, но значение будет предыдущим, а не тем, которое установлено.

Вот код, который я использую:

(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);
user2075039
источник
Это хорошо, но можете ли вы показать, как вы можете ограничить его только одним конкретным входом?
августа
@jwebb Не совсем уверен, что ты имеешь в виду? Мы переопределяем функцию jQuery val (). Вы привязываете обработчик события изменения к определенному входу.
user2075039
Я имею в виду, @ user2075039, что если на странице есть несколько входов, использующих функцию jQuery val (), и вы не хотите связывать обработчик пользовательских изменений со ВСЕМИ, а только с одним из них?
августа
@jwebb вы можете проверить this.selectorвнутри переопределяющей $.fn.valфункции для конкретного селектора (будет зависеть от того, что используется для селектора). Это лучшее, что я нашел для этого только для определенных полей, единственная проблема в том, что вы должны знать, что они используют для селектора
sMyles
1
$(":input#single").trigger('change');

Это сработало для моего сценария. У меня есть 3 комбо и связывание с событием chainSelect, мне нужно передать 3 значения по URL и по умолчанию выбрать все выпадающий список. Я использовал это

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

И первое событие сработало.

Прашант Патил
источник
10
Я надеюсь, вы никогда не делаете это в реальном мире. Само собой разумеется, что $ _GET [«что угодно»] нельзя доверять.
Денис V
1

Если вы только что добавили опцию выбора в форму и хотите запустить событие изменения, я обнаружил, что требуется setTimeout, иначе jQuery не подхватит вновь добавленное поле выбора:

window.setTimeout(function() { jQuery('.languagedisplay').change();}, 1);
Дейв Хилдич
источник