ErikPhipps
ErikPhipps

Reputation: 155

jQuery change event firing on select when arrows pressed

I've seen in numerous places that jQuery's change event on a select will only fire on blur or mouse-click, yet when I try it out, the event also fires on every arrow key press. I'm trying to get the change event to not fire when arrows are used to select an option. For example:

<select size="4" id="more" name="more" required="required">
    <option value="0">No</option>
    <option value="1">Yes</option>
    <option value="2">Maybe</option>
    <option value="3">No clue</option>
</select>

$('#more').on('change', function(event){
    alert('hi');
});​

This fiddle demonstrates the event firing when: http://jsfiddle.net/McWatt/x5cTr/7/

According to the jQuery change() page, if I tab to the select, then use the arrows to select an option, the change event should not fire until the select loses focus (at least that is how I am reading it). In reality, I am seeing the change event fire with every arrow key press.

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

For select boxes, checkboxes, and radio buttons, the event is fired immediately when the user makes a selection with the mouse, but for the other element types the event is deferred until the element loses focus.

Has this behavior changed, or am I not understanding the functionality?

Update: I was misreading the jQuery docs, although they could be clearer. I've accepted the answer that led me to that realization. For the actual solution to my problem, I bound a keydown event to the select that set a data attribute to false if the arrows were used. Then in the change binding, I set a condition to only execute code on change if the data attribute was true. This isn't the best solution as it effectively killed the change callback code if an arrow was used, but since there is an alternate way to trigger the functionality (via a button), I could live with this functionality.

Update2: Here is a working fiddle: http://jsfiddle.net/McWatt/BR8QQ/

// prevent change event being fired on arrow keys
$('#more').data('activation', 'activated').bind({
    keydown: function(event) {
        if(event.which === 38 || event.which === 40){
            $(this).data('activation', 'paused');
        }
    },
    click: function(event) {
        if($(this).data('activation') === 'paused'){
            $(this).data('activation', 'activated');
            $(this).trigger('change');
        }
    },
    change: function(event) {    
        if($(this).data('activation') === 'activated'){
            var time = new Date();
            $('#changed').text(event.type + ": " + time.getTime());
        }
    }
});

Upvotes: 3

Views: 3361

Answers (3)

Flo Develop
Flo Develop

Reputation: 765

The change event is not fired when an option is selected programmatically. So we can try to disable the change event when pressing arrow keys then add our behavior without interfering with other changes event previously defined :

/**
 * Prevent change event being fired on arrow keys.
 */
function PreventSelectChangeArrows() {
  console.log('PreventSelectChangeArrows');

  $('select').keydown(function (e) {
    if (e.keyCode === 38 || e.keyCode === 40) {
      e.preventDefault();

      // Select option programmatically to avoid change event being fired.
      let $select = $(this);
      let $options = $select.find('option');
      let $selected = $select.find('option:selected');
      let index = $options.index($selected);
      let nextIndex = index + (e.keyCode === 38 ? -1 : 1);
      let $next = $options.eq(nextIndex);
      if ($next.length) {
        $next.prop('selected', true);
      }
    }

    // Trigger on change event when pressing spacebar.
    if (e.keyCode === 32) {
      $(this).trigger('change');
      e.preventDefault();
    }

    // Trigger on change event when pressing tab.
    if (e.key === 'Tab' ) {
      $(this).trigger('change');
    }
  });
}

In my example you need to press spacebar or tab to trigger change event.

This code works on Chrome, didn't tested it on the other browsers.

Upvotes: 0

eleven11
eleven11

Reputation: 362

Maybe you could add a click check in there too

$('#more').change(function(event){
    $(this).click(function(event){
    var time = new Date();
    $('#changed').text(event.type + ": " + time.getTime());
    });
 });​

http://jsfiddle.net/x5cTr/13/

Upvotes: 1

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324760

Looks like it's inaccurate. As far as I can tell it's no different to the vanilla change event.

Basically, whenever a text-based input loses focus after its value is changed, it fires the event. Whenever a checkbox is clicked (or toggled with the spacebar when focussed) it fires the event. Whenever a radio button is selected (by clicking, spacebar, arrows keys...) it fires the event. Whenever a select box is changed (by selecting, arrow keys...) it fires the event too.

jQuery is no different to vanilla JS in this aspect.

Upvotes: 3

Related Questions