summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlou <louiscuny@gmail.com>2013-06-17 10:56:08 +0200
committerlou <louiscuny@gmail.com>2013-06-17 10:56:08 +0200
commit6b543c132f5f16f39ea3449fa29cb107117d3767 (patch)
tree7b6ffdfd05cb9346c4e15d2699c447954ebab4c4
parent3abad28c00247292817809b64e48c09c718df4cc (diff)
downloadmulti-select-6b543c132f5f16f39ea3449fa29cb107117d3767.zip
multi-select-6b543c132f5f16f39ea3449fa29cb107117d3767.tar.gz
multi-select-6b543c132f5f16f39ea3449fa29cb107117d3767.tar.bz2
refactor keyboard and use tabindex
-rw-r--r--css/multi-select.css1
-rw-r--r--js/application.js16
-rw-r--r--js/jquery.multi-select.js247
3 files changed, 152 insertions, 112 deletions
diff --git a/css/multi-select.css b/css/multi-select.css
index 8cdae1d..9987d25 100644
--- a/css/multi-select.css
+++ b/css/multi-select.css
@@ -63,6 +63,7 @@
}
.ms-container ul.ms-list{
+ position: relative;
width: 160px;
height: 200px;
padding: 0;
diff --git a/js/application.js b/js/application.js
index ab47ff1..a455d46 100644
--- a/js/application.js
+++ b/js/application.js
@@ -8,10 +8,11 @@
});
$('#searchable').multiSelect({
- selectableHeader: "<input type='text' id='search' autocomplete='off' placeholder='try \"elem 2\"'>"
+ selectableHeader: "<input type='text' id='search1' autocomplete='off' placeholder='try \"elem 2\"'>",
+ selectionHeader: "<input type='text' id='search2' autocomplete='off' placeholder='try \"elem 2\"'>"
});
- $('#search').quicksearch($('.ms-elem-selectable', '#ms-searchable' )).on('keydown', function(e){
+ $('#search1').quicksearch($('.ms-elem-selectable', '#ms-searchable' )).on('keydown', function(e){
if (e.keyCode == 40){
$(this).trigger('focusout');
$('#searchable').focus();
@@ -19,6 +20,15 @@
}
});
+ $('#search2').quicksearch($('.ms-elem-selection', '#ms-searchable' )).on('keydown', function(e){
+ if (e.keyCode == 40){
+ $(this).trigger('focusout');
+ console.log('focus selection')
+ $('.ms-selection', '#searchable').focus();
+ return false;
+ }
+ });
+
$('#optgroup').multiSelect({
selectableOptgroup: true
@@ -64,7 +74,7 @@
}
$('#select-100').click(function(){
- $('#public-methods').multiSelect('select', arr);
+ $('#public-methods').multiSelect('select', 1);
return false;
});
$('#deselect-100').click(function(){
diff --git a/js/jquery.multi-select.js b/js/jquery.multi-select.js
index ff37d66..2842a4e 100644
--- a/js/jquery.multi-select.js
+++ b/js/jquery.multi-select.js
@@ -26,10 +26,11 @@
this.$container = $('<div/>', { 'id': "ms-"+id, 'class': "ms-container" });
this.$selectableContainer = $('<div/>', { 'class': 'ms-selectable' });
this.$selectionContainer = $('<div/>', { 'class': 'ms-selection' });
- this.$selectableUl = $('<ul/>', { 'class': "ms-list" });
- this.$selectionUl = $('<ul/>', { 'class': "ms-list" });
+ this.$selectableUl = $('<ul/>', { 'class': "ms-list", 'tabindex' : '0' });
+ this.$selectionUl = $('<ul/>', { 'class': "ms-list", 'tabindex' : '-1' });
this.scrollTo = 0;
this.sanitizeRegexp = new RegExp("\\W+", 'gi');
+ this.elemsSelector = 'li:visible:not(.ms-optgroup-label,.ms-optgroup-container)';
};
MultiSelect.prototype = {
@@ -79,6 +80,7 @@
.append($(optgroupUlTemplate)
.append(optgroupSelectableLi));
+ that.$selectableUl.data('ms-mouse-active', false);
that.$selectableUl.append(optgroupSelectable);
optgroupSelectionLi.html(optgroupLabel);
@@ -87,6 +89,7 @@
.append($(optgroupUlTemplate)
.append(optgroupSelectionLi));
+ that.$selectionUl.data('ms-mouse-active', false);
that.$selectionUl.append(optgroupSelection);
optgroupCpt++;
@@ -160,12 +163,9 @@
that.$container.append(that.$selectableContainer);
that.$container.append(that.$selectionContainer);
ms.after(that.$container);
- that.$selectableUl.on('mouseenter', '.ms-elem-selectable', function(){
- $('li', that.$container).removeClass('ms-hover');
- $(this).addClass('ms-hover');
- }).on('mouseleave', function(){
- $('li', that.$container).removeClass('ms-hover');
- });
+
+ that.activeMouse(that.$selectableUl);
+ that.activeKeyboard(that.$selectableUl);
var action = that.options.dblClick ? 'dblclick' : 'click';
@@ -176,119 +176,127 @@
that.deselect($(this).data('ms-value'));
});
+ that.activeMouse(that.$selectionUl);
+ that.activeKeyboard(that.$selectionUl);
+ }
- that.$selectionUl.on('mouseenter', '.ms-elem-selection', function(){
- $('li', that.$selectionUl).removeClass('ms-hover');
- $(this).addClass('ms-hover');
- }).on('mouseleave', function(){
- $('li', that.$selectionUl).removeClass('ms-hover');
- });
+ var selectedValues = ms.find('option:selected').map(function(){ return $(this).val(); }).get();
+ that.select(selectedValues, 'init');
- that.$selectableUl.on('focusin', function(){
- $(this).addClass('ms-focus');
- that.$selectionUl.focusout();
- }).on('focusout', function(){
- $(this).removeClass('ms-focus');
- $('li', that.$container).removeClass('ms-hover');
- });
+ if (typeof that.options.afterInit === 'function') {
+ that.options.afterInit.call(this, this.$container);
+ }
+ },
- that.$selectionUl.on('focusin', function(){
- $(this).addClass('ms-focus');
- }).on('focusout', function(){
- $(this).removeClass('ms-focus');
- $('li', that.$container).removeClass('ms-hover');
- });
+ 'activeKeyboard' : function($list){
+ var that = this;
+
+ $list.on('focus', function(){
+ $(this).addClass('ms-focus');
+ })
+ .on('blur', function(){
+ $(this).removeClass('ms-focus');
+ })
+ .on('keydown', function(e){
+ switch (e.which) {
+ case 40:
+ case 38:
+ e.preventDefault();
+ e.stopPropagation();
+ that.moveHighlight($(this), (e.which === 38) ? -1 : 1);
+ return;
+ case 32:
+ e.preventDefault();
+ e.stopPropagation();
+ that.selectHighlighted($list);
+ return;
+ case 37:
+ case 39:
+ e.preventDefault();
+ e.stopPropagation();
+ that.switchList($list);
+ return;
+ }
+ });
+ },
- ms.on('focusin', function(){
- ms.focusout();
- that.$selectableUl.focusin();
- }).on('focusout', function(){
- that.$selectableUl.removeClass('ms-focus');
- that.$selectionUl.removeClass('ms-focus');
- });
+ 'moveHighlight': function($list, direction){
+ var $elems = $list.find(this.elemsSelector),
+ $currElem = $elems.filter('.ms-hover'),
+ $nextElem = null,
+ elemHeight = $elems.first().outerHeight(),
+ containerHeight = $list.height(),
+ containerSelector = '#'+this.$container.prop('id');
+
+ // Deactive mouseenter event when move is active
+ // It fixes a bug when mouse is over the list
+ $elems.off('mouseenter');
+ $list.data('ms-mouse-active', false);
+
+ $elems.removeClass('ms-hover');
+ if (direction === 1){ // DOWN
+ var nextSelector = containerSelector+' .'+$list.parent().prop('class')+' '+this.elemsSelector;
+
+ $nextElem = $currElem.nextALL(nextSelector).first();
+ if ($nextElem.length === 0){
+ $nextElem = $elems.first();
+ }
+ } else if (direction === -1){ // UP
+ var prevSelector = containerSelector+' .'+$list.parent().prop('class')+' '+this.elemsSelector;
- ms.onKeyDown = function(e, keyContainer){
- var ul = that.$container.find('.'+keyContainer).find('.ms-list'),
- lis = ul.find('li:visible:not(.ms-optgroup-label, .ms-optgroup-container)'),
- lisNumber = lis.length,
- liFocused = ul.find('li.ms-hover'),
- liFocusedIndex = liFocused.length > 0 ? lis.index(liFocused) : -1,
- ulHeight = ul.innerHeight(),
- liHeight = lis.first().outerHeight(true),
- numberOfLisDisplayed = Math.floor(ulHeight / liHeight),
- ulPosition = null;
-
- if (e.keyCode === 32){ // space
- if (liFocused.length >0){
- if (keyContainer === 'ms-selectable'){
- that.select(liFocused.data('ms-value'));
- } else {
- that.deselect(liFocused.data('ms-value'));
- }
- lis.removeClass('ms-hover');
- that.scrollTo = 0;
- ul.scrollTop(that.scrollTo);
- }
- } else if (e.keyCode === 40){ // Down
- if (lis.length > 0){
- var nextLiIndex = liFocusedIndex+1,
- nextLi = (lisNumber !== nextLiIndex) ? lis.eq(nextLiIndex) : lis.first(),
- nextLiPosition = nextLi.position().top;
-
- ulPosition = ul.position().top;
- lis.removeClass('ms-hover');
- nextLi.addClass('ms-hover');
-
- if (lisNumber === nextLiIndex){
- that.scrollTo = 0;
- } else if (nextLiPosition >= (ulPosition + (numberOfLisDisplayed * liHeight))){
- that.scrollTo += liHeight;
- }
- ul.scrollTop(that.scrollTo);
- }
- } else if (e.keyCode === 38){ // Up
- if (lis.length > 0){
- var prevLiIndex = Math.max(liFocusedIndex-1, -1),
- prevLi = lis.eq(prevLiIndex),
- prevLiPosition = prevLi.position().top;
-
- ulPosition = ul.position().top;
- lis.removeClass('ms-hover');
- prevLi.addClass('ms-hover');
- if (prevLiPosition <= ulPosition){
- that.scrollTo -= liHeight;
- } else if (prevLiIndex < 0){
- that.scrollTo = (lisNumber - numberOfLisDisplayed) * liHeight;
- }
- ul.scrollTop(that.scrollTo);
- }
- } else if (e.keyCode === 37 || e.keyCode === 39){
- if (that.$selectableUl.hasClass('ms-focus')){
- that.$selectableUl.focusout();
- that.$selectionUl.focusin();
- } else {
- that.$selectableUl.focusin();
- that.$selectionUl.focusout();
- }
- }
- };
+ $nextElem = $currElem.prevALL(prevSelector).first();
+ if ($nextElem.length === 0){
+ $nextElem = $elems.last();
+ }
+ }
+ if ($nextElem.length > 0){
+ $nextElem.addClass('ms-hover');
+ var scrollTo = $list.scrollTop() + $nextElem.position().top -
+ containerHeight / 2 + elemHeight / 2;
- ms.on('keydown', function(e){
- if (ms.is(':focus')){
- var keyContainer = that.$selectableUl.hasClass('ms-focus') ? 'ms-selectable' : 'ms-selection';
- ms.onKeyDown(e, keyContainer);
- }
- });
+ $list.scrollTop(scrollTo);
}
+ },
- var selectedValues = ms.find('option:selected').map(function(){ return $(this).val(); }).get();
- that.select(selectedValues, 'init');
+ 'selectHighlighted' : function($list){
+ var $elems = $list.find(this.elemsSelector),
+ $highlightedElem = $elems.filter('.ms-hover').first();
- if (typeof that.options.afterInit === 'function') {
- that.options.afterInit.call(this, this.$container);
+ if ($highlightedElem.length > 0){
+ if ($list.parent().hasClass('ms-selectable')){
+ this.select($highlightedElem.data('ms-value'));
+ } else {
+ this.deselect($highlightedElem.data('ms-value'));
+ }
+ $elems.removeClass('ms-hover');
+ }
+ },
+
+ 'switchList' : function($list){
+ $list.blur();
+ if ($list.parent().hasClass('ms-selectable')){
+ this.$selectionUl.focus();
+ } else {
+ this.$selectableUl.focus();
}
},
+ 'activeMouse' : function($list){
+ var that = this;
+
+ $list.on('mousemove', function(){
+ if ($list.data('ms-mouse-active') === false){
+ var elems = $list.find(that.elemsSelector);
+
+ elems.on('mouseenter', function(){
+ elems.removeClass('ms-hover');
+ $(this).addClass('ms-hover');
+ $list.data('ms-mouse-active', true);
+ });
+ }
+ });
+ },
+
'refresh' : function() {
this.destroy();
this.$element.multiSelect(this.options);
@@ -464,4 +472,25 @@
$.fn.multiSelect.Constructor = MultiSelect;
+ // Lovely borrowed from http://stackoverflow.com/a/324159/195571
+ $.fn.reverse = function() {
+ return this.pushStack(this.get().reverse(), arguments);
+ };
+
+ $.each( ['prev', 'next'], function(unusedIndex, name) {
+ $.fn[ name + 'ALL' ] = function(matchExpr) {
+ // get all the elements in the body, including the body.
+ var $all = $('body').find('*').andSelf();
+
+ // slice the $all object according to which way we're looking
+ $all = (name == 'prev')
+ ? $all.slice(0, $all.index(this)).reverse()
+ : $all.slice($all.index(this) + 1)
+ ;
+ // filter the matches if specified
+ if (matchExpr) $all = $all.filter(matchExpr);
+ return $all;
+ };
+ });
+
}(window.jQuery); \ No newline at end of file