Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrow keys do not change value of select #3472

Open
OtherCroissant opened this issue Jun 12, 2015 · 33 comments
Open

Arrow keys do not change value of select #3472

OtherCroissant opened this issue Jun 12, 2015 · 33 comments

Comments

@OtherCroissant
Copy link

According to #3279 the feature of opening the select2 search dropdown is not implemented in the newer version according to technical difficulties, but also because it does not reflect the default behaviour of a native select.

Though, following this native select behaviour, the user should be able to browse through the dropdown items by using the arrow keys on the keyboard. This does not seem to work for the select2 4.0 implementation. Is there a reason for this?

@OtherCroissant OtherCroissant changed the title Arrow down does not change value of select Arrow keys do not change value of select Jun 12, 2015
@AlexBuitrago
Copy link

+1000

@rain01
Copy link

rain01 commented Jul 31, 2015

yes please

@sidharthc
Copy link

Is there a solution for this?

@uzlov
Copy link

uzlov commented Aug 18, 2015

this.on('keypress', function (evt) {
  var key = evt.which;

  if (self.isOpen()) {
    if (key === KEYS.ENTER) {
      self.trigger('results:select');

      evt.preventDefault();
    } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
      self.trigger('results:toggle');

      evt.preventDefault();
    } else if (key === KEYS.UP) {
      self.trigger('results:previous');

      evt.preventDefault();
    } else if (key === KEYS.DOWN) {
      self.trigger('results:next');

      evt.preventDefault();
    } else if (key === KEYS.ESC || key === KEYS.TAB) {
      self.close();

      evt.preventDefault();
    }
  } else {
    if (key === KEYS.ENTER || key === KEYS.SPACE ||
        ((key === KEYS.DOWN || key === KEYS.UP) && evt.altKey)) {
      self.open();

      evt.preventDefault();
    }
    if (key === KEYS.DOWN) {
      if (undefined != this.$element.find('option:selected').next().val()) {
        this.$element.val(this.$element.find('option:selected').next().val());
        this.$element.trigger('change');
      }
      evt.preventDefault();
    }
    if (key === KEYS.UP) {
      if (undefined != this.$element.find('option:selected').prev().val()) {
        this.$element.val(this.$element.find('option:selected').prev().val());
        this.$element.trigger('change');
      }
      evt.preventDefault();
    }
  }
});

@rain01
Copy link

rain01 commented Aug 18, 2015

@uzlov what is this? where do you put it?

@sidharthc
Copy link

@uzlov seems good.

@uzlov
Copy link

uzlov commented Aug 19, 2015

@rain01
it's a patch for select2
please, find function this.on('keypress', function (evt) {
and replace with my code

@alvarotrigo
Copy link

+1

@OtherCroissant
Copy link
Author

@uzlov could you submit a PR?

@alvarotrigo
Copy link

I believe the change @uzlov suggests have to be made in line 323 of core.js.

An easy change that improves the usability and accessibility of the plugin. In my case my boss wouldn't allow me to use it unless this feature was provided.

Still need to fix the autofocus, but hopefully I'll manage to do it.

@alvarotrigo
Copy link

Or if you prefer, line 5150 of the unminified distributed file.

@sauron918
Copy link

+1

1 similar comment
@kortsmit
Copy link

kortsmit commented Nov 5, 2015

+1

@bradparks
Copy link

For sure, +1. I'm surprised this is an issue still !

@alexyorke
Copy link
Contributor

+1

1 similar comment
@wsromek
Copy link

wsromek commented Nov 16, 2015

👍

@WimDeMeester
Copy link

@uzlov 's code is working fine for me. Any change that this will be included in the next version (4.0.2?) of select2?

@phazei
Copy link

phazei commented Jan 22, 2016

I've been using @uzlov code, rather than modifying the code I just go:

                    s2Obj = $(element).data('select2');
                    delete s2Obj.listeners.keypress;
                    s2Obj.on('keypress', function (evt) {
                        //his code here
                    });

But I also just found an issue with it. If the item in the select list is disabled, and you arrow to it, it actually selects it instead of going to the next valid option. It doesn't really select it, but it shows in the box. My validation marks it as invalid so it's sort of ok, but it should be next active item.

I think on("results:next") likely has a bug
See next comment, found issue with bug

@phazei
Copy link

phazei commented Feb 23, 2016

Note: in @uzlov code, the KEYS.DOWN and KEYS.UP items should be changed to only select the enabled options:

if (key === KEYS.DOWN) {
    var val = s2Obj.$element.find('option:selected').nextAll(":enabled").first().val();
    if (undefined !== val) {
        s2Obj.$element.val(val);
        s2Obj.$element.trigger('change');
    }
    evt.preventDefault();
}
if (key === KEYS.UP) {
    var val = s2Obj.$element.find('option:selected').prevAll(":enabled").first().val();
    if (undefined !== val) {
        s2Obj.$element.val(val);
        s2Obj.$element.trigger('change');
    }
    evt.preventDefault();
}

@lucjan
Copy link

lucjan commented Apr 13, 2016

+1

@alexweissman
Copy link
Contributor

alexweissman commented Dec 16, 2017

It's worth noting that if you hold Alt while pressing the arrow keys, you will be able to scroll through options. Not sure if this is best solution, but it does work.

#4293 and #5130 attempt to resolve this issue in a few other ways.

@aaime
Copy link

aaime commented Jan 28, 2018

+1

@KyleMit
Copy link

KyleMit commented Mar 3, 2018

It's probably best to not modify the library contents ad-hoc (and especially difficult if you're serving from a CDN). You can implement uzlov's suggestion (and phazei's update) by attaching a listener to the object after the library has already loaded. You can listen for key strokes on the .select2 input when it's closed or the .select2-dropdown if it's open. And then traverse the dom to find the element and update accordingly after the fact like this:

 // listen for keyups in both input widget AND dropdown
$("body").on('keyup', ".select2,.select2-dropdown", function (e) {
  var KEYS = { UP: 38, DOWN: 40 };
  var $sel2 = $(this).closest(".select2");
  // if we can't find it by traveersing the dom, search page for an open container
  // ASSUMES only one open at a time - 
  // don't really know how to cross walk from dropdown back to coresponding element
  if ($sel2.length == 0) {
  	$sel2 = $(".select2.select2-container--open");
  }

  // make sure we found the <select> el
  var $sel = $sel2.data("element")
  if ($sel.length) {
    var newValue

    if (e.keyCode === KEYS.DOWN && !e.altKey) {
      newValue = $sel.find('option:selected').nextAll(":enabled").first().val();
    } else if (e.keyCode === KEYS.UP) {
      newValue = $sel.find('option:selected').prevAll(":enabled").first().val();
    }

    // if we got a value, set it and update
    if (newValue != undefined) {
      $sel.val(newValue);
      $sel.trigger('change');
    }
  }

});

Demo in jsFiddle

@kfrz
Copy link

kfrz commented Mar 19, 2018

@KyleMit this is a nice solution. I'm also of the "I don't want to to modify core lib code" camp, so until #4293 is merged this workaround will suffice.

The only thing I've noticed is there's no e.preventDefault() which means when selecting elements with arrow key, the page will scroll -- however I cannot seem to figure where to place the preventDefault() to make it work. Any suggestions?

@ghost
Copy link

ghost commented Jul 12, 2018

@kfrz What worked for me was changing over to a keydown event, instead of the keyup suggested above.

@stale
Copy link

stale bot commented Mar 13, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the status: stale label Mar 13, 2019
@stale stale bot closed this as completed Mar 20, 2019
@kevin-brown kevin-brown reopened this Feb 5, 2020
@stale stale bot removed the status: stale label Feb 5, 2020
@avernet
Copy link

avernet commented Nov 17, 2021

+1

@Jagdish-J-P
Copy link

Any update on this?

@zpiecuch81
Copy link

Any update on this?
I've just hit similar issue. Was it fixed in any version?

@PBrockmann
Copy link

Any news on this issue ? Have not been able to find a solution with 4.0.13 release.
Crudely missing this default feature that is available with a classic select !

@PBrockmann
Copy link

Here is a siple solution that runs with select2 4.0.13

$("body").on('keyup', ".select2", function(e) {
  var KEYS = {UP: 38, DOWN: 40};
  var sel = $(this).closest('.select2-container').parent().find('select');
  if (e.keyCode === KEYS.DOWN) {
    newValue = $(sel).find(':selected').next().val();
  } else if (e.keyCode === KEYS.UP) {
    newValue = $(sel).find(':selected').prev().val();
  }
  if (newValue != undefined) {
    $(sel).val(newValue).trigger('change');
  }
});

The jsfiddle is available from https://jsfiddle.net/PBrockmann/ktcnr6o7/

@nickcoast
Copy link

@PBrockmann That works great. The only issues I had were:

  1. The entire viewport scrolls when hitting UP and DOWN arrows. Adding e.preventDefault() fixes that for me.
  2. Pressing and holding UP or DOWN only scroll by one value, instead of scrolling continuously. So I changed 'keyup' to 'keydown'.

$("body").on('keydown', ".select2", function(e) { // changed to 'keydown'
  var KEYS = {UP: 38, DOWN: 40};
  var sel = $(this).closest('.select2-container').parent().find('select');
  var newValue;

  if (e.keyCode === KEYS.DOWN) {
    e.preventDefault(); // Prevent the whole viewport from scrolling
    newValue = $(sel).find(':selected').next().val();
  } else if (e.keyCode === KEYS.UP) {
    e.preventDefault(); // Prevent the whole viewport from scrolling
    newValue = $(sel).find(':selected').prev().val();
  }
  if (newValue !== undefined) {
    $(sel).val(newValue).trigger('change');
  }
});

@PBrockmann
Copy link

Thank you for the feedback but I do not have the same behaviour with google-chrome.
No need to add e.preventDefault().

Could you precise what browser is concerned ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests