From 8dfd6d696021d16314ac1616605c82059d40ab0a Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Sat, 18 Oct 2014 11:28:42 -0400 Subject: [PATCH] Add more keyboard support When the main container is focused, you can now move the highlighted result by using the up and down arrows on the keyboard. This does not yet keep the highlighted result visible at all times, and it only is implemented on single selects, but that will come in the future. --- dist/js/select2.amd.full.js | 57 ++++++++++++++++++++++++++++++ dist/js/select2.amd.js | 57 ++++++++++++++++++++++++++++++ dist/js/select2.full.js | 57 ++++++++++++++++++++++++++++++ dist/js/select2.full.min.js | 4 +-- dist/js/select2.js | 57 ++++++++++++++++++++++++++++++ dist/js/select2.min.js | 2 +- src/js/select2/core.js | 6 ++++ src/js/select2/results.js | 47 ++++++++++++++++++++++++ src/js/select2/selection/single.js | 4 +++ 9 files changed, 288 insertions(+), 3 deletions(-) diff --git a/dist/js/select2.amd.full.js b/dist/js/select2.amd.full.js index 7f966006..e312499c 100644 --- a/dist/js/select2.amd.full.js +++ b/dist/js/select2.amd.full.js @@ -305,6 +305,10 @@ define('select2/results',[ container.on('results:select', function () { var $highlighted = self.$results.find('.highlighted'); + if ($highlighted.length === 0) { + return; + } + var data = $highlighted.data('data'); if ($highlighted.attr('aria-selected') == 'true') { @@ -318,6 +322,49 @@ define('select2/results',[ } }); + container.on('results:previous', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + // If we are already at te top, don't move further + if (currentIndex === 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + + container.on('results:next', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + this.$results.on('mouseup', '.option[aria-selected]', function (evt) { var $this = $(this); @@ -477,6 +524,10 @@ define('select2/selection/single',[ if (container.isOpen()) { if (key == KEYS.ENTER) { self.trigger('results:select'); + } else if (key == KEYS.UP) { + self.trigger('results:previous'); + } else if (key == KEYS.DOWN) { + self.trigger('results:next'); } } else { if (key == KEYS.ENTER || key == KEYS.SPACE) { @@ -1239,6 +1290,12 @@ define('select2/core',[ this.selection.on('results:select', function () { self.trigger('results:select'); }); + this.selection.on('results:previous', function () { + self.trigger('results:previous'); + }); + this.selection.on('results:next', function () { + self.trigger('results:next'); + }); this.selection.on('unselected', function (params) { self.trigger('unselect', params); diff --git a/dist/js/select2.amd.js b/dist/js/select2.amd.js index 7f966006..e312499c 100644 --- a/dist/js/select2.amd.js +++ b/dist/js/select2.amd.js @@ -305,6 +305,10 @@ define('select2/results',[ container.on('results:select', function () { var $highlighted = self.$results.find('.highlighted'); + if ($highlighted.length === 0) { + return; + } + var data = $highlighted.data('data'); if ($highlighted.attr('aria-selected') == 'true') { @@ -318,6 +322,49 @@ define('select2/results',[ } }); + container.on('results:previous', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + // If we are already at te top, don't move further + if (currentIndex === 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + + container.on('results:next', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + this.$results.on('mouseup', '.option[aria-selected]', function (evt) { var $this = $(this); @@ -477,6 +524,10 @@ define('select2/selection/single',[ if (container.isOpen()) { if (key == KEYS.ENTER) { self.trigger('results:select'); + } else if (key == KEYS.UP) { + self.trigger('results:previous'); + } else if (key == KEYS.DOWN) { + self.trigger('results:next'); } } else { if (key == KEYS.ENTER || key == KEYS.SPACE) { @@ -1239,6 +1290,12 @@ define('select2/core',[ this.selection.on('results:select', function () { self.trigger('results:select'); }); + this.selection.on('results:previous', function () { + self.trigger('results:previous'); + }); + this.selection.on('results:next', function () { + self.trigger('results:next'); + }); this.selection.on('unselected', function (params) { self.trigger('unselect', params); diff --git a/dist/js/select2.full.js b/dist/js/select2.full.js index 907187e2..9009fad9 100644 --- a/dist/js/select2.full.js +++ b/dist/js/select2.full.js @@ -9843,6 +9843,10 @@ define('select2/results',[ container.on('results:select', function () { var $highlighted = self.$results.find('.highlighted'); + if ($highlighted.length === 0) { + return; + } + var data = $highlighted.data('data'); if ($highlighted.attr('aria-selected') == 'true') { @@ -9856,6 +9860,49 @@ define('select2/results',[ } }); + container.on('results:previous', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + // If we are already at te top, don't move further + if (currentIndex === 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + + container.on('results:next', function () { + var $highlighted = self.$results.find('.highlighted'); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + }); + this.$results.on('mouseup', '.option[aria-selected]', function (evt) { var $this = $(this); @@ -10015,6 +10062,10 @@ define('select2/selection/single',[ if (container.isOpen()) { if (key == KEYS.ENTER) { self.trigger('results:select'); + } else if (key == KEYS.UP) { + self.trigger('results:previous'); + } else if (key == KEYS.DOWN) { + self.trigger('results:next'); } } else { if (key == KEYS.ENTER || key == KEYS.SPACE) { @@ -10777,6 +10828,12 @@ define('select2/core',[ this.selection.on('results:select', function () { self.trigger('results:select'); }); + this.selection.on('results:previous', function () { + self.trigger('results:previous'); + }); + this.selection.on('results:next', function () { + self.trigger('results:next'); + }); this.selection.on('unselected', function (params) { self.trigger('unselect', params); diff --git a/dist/js/select2.full.min.js b/dist/js/select2.full.min.js index 55dfd2b7..820b2033 100644 --- a/dist/js/select2.full.min.js +++ b/dist/js/select2.full.min.js @@ -1,4 +1,4 @@ var requirejs,require,define;!function(a){function b(a,b){return r.call(a,b)}function c(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=p.map,q=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,p.nodeIdCompat&&t.test(a[g])&&(a[g]=a[g].replace(t,"")),a=n.concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||q)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&q&&q[d]&&(i=q[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function d(b,c){return function(){return k.apply(a,s.call(arguments,0).concat([b,c]))}}function e(a){return function(b){return c(b,a)}}function f(a){return function(b){n[a]=b}}function g(c){if(b(o,c)){var d=o[c];delete o[c],q[c]=!0,j.apply(a,d)}if(!b(n,c)&&!b(q,c))throw new Error("No "+c);return n[c]}function h(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function i(a){return function(){return p&&p.config&&p.config[a]||{}}}var j,k,l,m,n={},o={},p={},q={},r=Object.prototype.hasOwnProperty,s=[].slice,t=/\.js$/;l=function(a,b){var d,f=h(a),i=f[0];return a=f[1],i&&(i=c(i,b),d=g(i)),i?a=d&&d.normalize?d.normalize(a,e(b)):c(a,b):(a=c(a,b),f=h(a),i=f[0],a=f[1],i&&(d=g(i))),{f:i?i+"!"+a:a,n:a,pr:i,p:d}},m={require:function(a){return d(a)},exports:function(a){var b=n[a];return"undefined"!=typeof b?b:n[a]={}},module:function(a){return{id:a,uri:"",exports:n[a],config:i(a)}}},j=function(c,e,h,i){var j,k,p,r,s,t,u=[],v=typeof h;if(i=i||c,"undefined"===v||"function"===v){for(e=!e.length&&h.length?["require","exports","module"]:e,s=0;s0&&b-1 in a}function d(a,b,c){if(ab.isFunction(b))return ab.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return ab.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(hb.test(b))return ab.filter(b,a,c);b=ab.filter(b,a)}return ab.grep(a,function(a){return U.call(b,a)>=0!==c})}function e(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function f(a){var b=ob[a]={};return ab.each(a.match(nb)||[],function(a,c){b[c]=!0}),b}function g(){$.removeEventListener("DOMContentLoaded",g,!1),a.removeEventListener("load",g,!1),ab.ready()}function h(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=ab.expando+Math.random()}function i(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(ub,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:tb.test(c)?ab.parseJSON(c):c}catch(e){}sb.set(a,b,c)}else c=void 0;return c}function j(){return!0}function k(){return!1}function l(){try{return $.activeElement}catch(a){}}function m(a,b){return ab.nodeName(a,"table")&&ab.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function n(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function o(a){var b=Kb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function p(a,b){for(var c=0,d=a.length;d>c;c++)rb.set(a[c],"globalEval",!b||rb.get(b[c],"globalEval"))}function q(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(rb.hasData(a)&&(f=rb.access(a),g=rb.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)ab.event.add(b,e,j[e][c])}sb.hasData(a)&&(h=sb.access(a),i=ab.extend({},h),sb.set(b,i))}}function r(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&ab.nodeName(a,b)?ab.merge([a],c):c}function s(a,b){var c=b.nodeName.toLowerCase();"input"===c&&yb.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function t(b,c){var d=ab(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:ab.css(d[0],"display");return d.detach(),e}function u(a){var b=$,c=Ob[a];return c||(c=t(a,b),"none"!==c&&c||(Nb=(Nb||ab("