Martijn
Martijn

Reputation: 3754

jQuery code triggered twice for bind('keyup change') when pressing key

I have a number input that should trigger jQuery on every change. To do this, I do a $('#id').bind('keyup change', ...);, which is triggered on any keyup or mouse-controller changed.

This works find for mouse clicks (triggers change) and for typing numbers (triggers keyup). But for the cursor keys it triggers both change and keyup. How can I make it so it only triggers once in all situations?

Here's an example showing the problem: http://jsfiddle.net/jSjkE/

Upvotes: 9

Views: 11403

Answers (3)

Nimesh Vaghasiya
Nimesh Vaghasiya

Reputation: 977

Keyup always works for input, search fields, and you can prevent change event as below: and keep change event working for other controls i.e. select.

// Prevent trigger change as keyup event already triggered so no twice call
if ((e.target.type == 'input' || e.target.type == 'search') && e.type == 'change') {
    return;
}

Upvotes: 0

lufi
lufi

Reputation: 670

$('input').unbind('keyup').bind('keyup',function(e){ ...

Upvotes: 0

Fabrício Matté
Fabrício Matté

Reputation: 70159

You have a wrong concept about the onchange event.

It does NOT fire when the element's value changes, instead, it fires only when 2 conditions are met:

  • The element blurs (loses focus);
  • The element has a different value than when it gained focus.

Note: The above is for inputs of type text. For types checkbox/radio it fires as soon as their checked property changes, and for select elements as soon as its selected option changes.

The onchange event (possibly wrongly) fires when you press Chrome's input type="number" up/down arrows. You should not rely on it, as many other major browsers don't even have an implementation of the number inputs yet and may not follow this behavior.

Instead, as you're using HTML5, you should rely on the oninput HTML5 event, which fires whenever the element's value is updated.

$(function() {
    var count = 0;
    $('#num').on('input', function(e) {
        console.log('update ' + (++count) + ' Value: ' + this.value);    
    });
});​

Fiddle

edit:

For older (and non-HTML5) browsers, you may use the keyup event as a fallback:

$('#num').on('input keyup', function(e) {

The most resilient way to do this for non-HTML5 browsers would be having a setInterval constantly checking if the element's value has changed, as keyup does not trigger if you select some text and drag it into the textbox - you could try .on('input keyup mouseup', but then it wouldn't trigger if the user uses the browser's Edit > Paste menu.

edit2:

If you don't want to use setInterval, you can put a huge events map inside the .on/.bind and check if the value has changed by storing the current value in the .data() of the element:

var count = 0;
$('#num').on('input keyup change', function(e) {
    if (this.value == $(this).data('curr_val'))
        return false;
    $(this).data('curr_val', this.value);

    console.log('update ' + (++count) + ' Value: ' + this.value);
});

Fiddle

I've added the onchange event to the events map, so even if the oninput and onkeyup fails (non-html5 browser using the browser's menu to paste data), the onchange event will still fire when the element loses focus.

Here's the final edit with a commented setInterval and .data() to avoid using globals, so you can choose which of the fallbacks to use:

$(function() {
    var $num = $('#num');
    $num.data('curr_val', $num.val()); //sets initial value

    var count = 0;
    setInterval(function(){
        if ($num.data('curr_val') == $num.val()) //if it still has same value
            return false; //returns false
        $num.data('curr_val', $num.val()); //if val is !=, updates it and
        //do your stuff
        console.log('update ' + (++count) + ' Value: ' + $num.val());
    }, 100); //100 = interval in miliseconds
});

Fiddle

Upvotes: 16

Related Questions