Skip to content

Commit 683ec2a

Browse files
committed
Accessibility improvements, closes #71
1 parent 18879e1 commit 683ec2a

File tree

8 files changed

+449
-353
lines changed

8 files changed

+449
-353
lines changed

.eslintrc

Lines changed: 240 additions & 240 deletions
Large diffs are not rendered by default.

gulpfile.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ gulp.task('lint', function() {
3131
.pipe($.eslint.format());
3232
});
3333

34-
gulp.task('js', ['bump', 'lint'], function() {
34+
gulp.task('js', ['lint'], function() {
3535
var pkg = getPackageJson(),
3636
banner = [
3737
'/*!',
@@ -58,7 +58,7 @@ gulp.task('js', ['bump', 'lint'], function() {
5858
.pipe($.connect.reload());
5959
});
6060

61-
gulp.task('js-min', ['bump'], function() {
61+
gulp.task('js-min', function() {
6262
var pkg = getPackageJson();
6363

6464
return gulp.src('src/jquery.selectric.js')
@@ -142,7 +142,7 @@ gulp.task('gh-pages', function() {
142142
/*======================================
143143
Default tasks
144144
======================================*/
145-
gulp.task('build', ['js', 'js-min', 'css']);
145+
gulp.task('build', ['bump', 'js', 'js-min', 'css']);
146146
gulp.task('default', ['build', 'watch']);
147147
gulp.task('release', ['bump', 'build', 'zip']);
148148
gulp.task('publish', ['gh-pages']);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
"url": "https://github.com/lcdsantos/jQuery-Selectric.git"
2929
},
3030
"dependencies": {
31-
"codecov": "^1.0.1",
3231
"jquery": "^3.0.0"
3332
},
3433
"devDependencies": {
34+
"codecov": "^1.0.1",
3535
"eslint": "^2.13.1",
3636
"gulp": "^3.9.1",
3737
"gulp-autoprefixer": "^3.1.0",

public/jquery.selectric.js

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
var $win = $(window);
4545

4646
var pluginName = 'selectric';
47-
var classList = 'Input Items Open Disabled TempShow HideSelect Wrapper Hover Responsive Above Scroll Group GroupLabel';
47+
var classList = 'Input Items Open Disabled TempShow HideSelect Wrapper Focus Hover Responsive Above Scroll Group GroupLabel';
4848
var bindSufix = '.sl';
4949

5050
var chars = ['a', 'e', 'i', 'o', 'u', 'n', 'c', 'y'];
@@ -101,11 +101,21 @@
101101
return /android|ip(hone|od|ad)/i.test(navigator.userAgent);
102102
},
103103

104+
/**
105+
* Escape especial characters in string (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
106+
*
107+
* @param {string} str - The string to be escaped
108+
* @return {string} The string with the special characters escaped
109+
*/
110+
escapeRegExp: function(str) {
111+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
112+
},
113+
104114
/**
105115
* Replace diacritics
106116
*
107-
* @param {string} str - The string to replace the diacritics.
108-
* @return {string} The string with diacritics replaced with ascii characters.
117+
* @param {string} str - The string to replace the diacritics
118+
* @return {string} The string with diacritics replaced with ascii characters
109119
*/
110120
replaceDiacritics: function(str) {
111121
var k = diacritics.length;
@@ -483,41 +493,35 @@
483493

484494
_this.elements.input
485495
.prop({ tabindex: _this.originalTabindex, disabled: false })
486-
.on('keypress' + bindSufix, _this.handleSystemKeys)
487-
.on('keydown' + bindSufix, function(e) {
488-
_this.handleSystemKeys(e);
496+
.on('keydown' + bindSufix, $.proxy(_this.handleKeys, _this))
497+
.on('focusin' + bindSufix, function(e) {
498+
_this.elements.outerWrapper.addClass(_this.classes.focus);
499+
500+
// Prevent the flicker when focusing out and back again in the browser window
501+
_this.elements.input.one('blur', function() {
502+
_this.elements.input.blur();
503+
});
504+
505+
if ( _this.options.openOnFocus && !_this.state.opened ) {
506+
_this.open(e);
507+
}
508+
})
509+
.on('focusout' + bindSufix, function() {
510+
_this.elements.outerWrapper.removeClass(_this.classes.focus);
511+
})
512+
.on('input propertychange', function() {
513+
var val = _this.elements.input.val();
489514

490515
// Clear search
491516
clearTimeout(_this.resetStr);
492517
_this.resetStr = setTimeout(function() {
493518
_this.elements.input.val('');
494519
}, _this.options.keySearchTimeout);
495520

496-
var key = e.keyCode || e.which;
497-
498-
// If it's a directional key
499-
// 37 => Left
500-
// 38 => Up
501-
// 39 => Right
502-
// 40 => Down
503-
if ( key > 36 && key < 41 ) {
504-
if ( !_this.options.allowWrap ) {
505-
if ( (key < 39 && _this.state.selectedIdx === 0) || (key > 38 && (_this.state.selectedIdx + 1) === _this.items.length) ) {
506-
return;
507-
}
508-
}
509-
510-
_this.select(_this.utils[(key < 39 ? 'previous' : 'next') + 'EnabledItem'](_this.items, _this.state.selectedIdx));
511-
}
512-
})
513-
.on('focusin' + bindSufix, function(e) {
514-
_this.state.opened || _this.open(e);
515-
})
516-
.on('oninput' in _this.elements.input[0] ? 'input' : 'keyup', function() {
517-
if ( _this.elements.input.val().length ) {
521+
if ( val.length ) {
518522
// Search in select options
519523
$.each(_this.items, function(i, elm) {
520-
if ( RegExp('^' + _this.elements.input.val(), 'i').test(elm.slug) && !elm.disabled ) {
524+
if ( RegExp('^' + _this.utils.escapeRegExp(val), 'i').test(elm.slug) && !elm.disabled ) {
521525
_this.select(i);
522526
return false;
523527
}
@@ -543,22 +547,54 @@
543547
},
544548

545549
/**
546-
* Behavior when system keys is pressed
550+
* Behavior when keyboard keys is pressed
547551
*
548552
* @param {object} e - Event object
549553
*/
550-
handleSystemKeys: function(e) {
554+
handleKeys: function(e) {
551555
var _this = this;
552556
var key = e.keyCode || e.which;
553-
554-
if ( key == 13 ) {
557+
var keys = _this.options.keys;
558+
559+
var isPrev = $.inArray(key, keys.previous) > -1;
560+
var isNext = $.inArray(key, keys.next) > -1;
561+
var isSelect = $.inArray(key, keys.select) > -1;
562+
var isOpen = $.inArray(key, keys.open) > -1;
563+
var idx = _this.state.selectedIdx;
564+
var isFirstOrLastItem = (isPrev && idx === 0) || (isNext && (idx + 1) === _this.items.length);
565+
var goToItem = 0;
566+
567+
// Enter / Space
568+
if ( key === 13 || key === 32 ) {
555569
e.preventDefault();
556570
}
557571

572+
// If it's a directional key
573+
if ( isPrev || isNext ) {
574+
if ( !_this.options.allowWrap && isFirstOrLastItem ) {
575+
return;
576+
}
577+
578+
if ( isPrev ) {
579+
goToItem = _this.utils.previousEnabledItem(_this.items, idx);
580+
}
581+
582+
if ( isNext ) {
583+
goToItem = _this.utils.nextEnabledItem(_this.items, idx);
584+
}
585+
586+
_this.select(goToItem);
587+
}
588+
558589
// Tab / Enter / ESC
559-
if ( /^(9|13|27)$/.test(key) ) {
560-
e.stopPropagation();
561-
_this.select(_this.state.selectedIdx, true);
590+
if ( isSelect && _this.state.opened ) {
591+
_this.select(idx, true);
592+
return;
593+
}
594+
595+
// Space / Enter / Left / Up / Right / Down
596+
if ( isOpen && !_this.state.opened ) {
597+
_this.open();
562598
}
563599
},
564600

@@ -845,23 +881,31 @@
845881
* @type {object}
846882
*/
847883
$.fn[pluginName].defaults = {
848-
onChange: function(elm) { $(elm).change(); },
849-
maxHeight: 300,
850-
keySearchTimeout: 500,
851-
arrowButtonMarkup: '<b class="button">&#x25be;</b>',
852-
disableOnMobile: true,
853-
openOnHover: false,
854-
hoverIntentTimeout: 500,
855-
expandToItemText: false,
856-
responsive: false,
857-
preventWindowScroll: true,
858-
inheritOriginalWidth: false,
859-
allowWrap: true,
860-
customClass: {
884+
onChange : function(elm) { $(elm).change(); },
885+
maxHeight : 300,
886+
keySearchTimeout : 500,
887+
arrowButtonMarkup : '<b class="button">&#x25be;</b>',
888+
disableOnMobile : true,
889+
openOnFocus : true,
890+
openOnHover : false,
891+
hoverIntentTimeout : 500,
892+
expandToItemText : false,
893+
responsive : false,
894+
preventWindowScroll : true,
895+
inheritOriginalWidth : false,
896+
allowWrap : true,
897+
optionsItemBuilder : '{text}', // function(itemData, element, index)
898+
labelBuilder : '{text}', // function(currItem)
899+
keys : {
900+
previous : [37, 38], // Left / Up
901+
next : [39, 40], // Right / Down
902+
select : [9, 13, 27], // Tab / Enter / Escape
903+
open : [13, 32, 37, 38, 39, 40], // Enter / Space / Left / Up / Right / Down
904+
close : [9, 27] // Tab / Escape
905+
},
906+
customClass : {
861907
prefix: pluginName,
862908
camelCase: false
863-
},
864-
optionsItemBuilder: '{text}', // function(itemData, element, index)
865-
labelBuilder: '{text}' // function(currItem)
909+
}
866910
};
867911
}));

public/jquery.selectric.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/selectric.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
border-bottom: none;
5959
}
6060

61+
.selectric-focus .selectric {
62+
border-color: #AAA;
63+
}
64+
6165
.selectric-hover .selectric {
6266
border-color: #C4C4C4;
6367
}
@@ -156,11 +160,11 @@
156160
cursor: pointer;
157161
}
158162
.selectric-items li.selected {
159-
background: #EFEFEF;
163+
background: #E0E0E0;
160164
color: #444;
161165
}
162166
.selectric-items li:hover {
163-
background: #F0F0F0;
167+
background: #D5D5D5;
164168
color: #444;
165169
}
166170
.selectric-items .disabled {

0 commit comments

Comments
 (0)