Prechádzať zdrojové kódy

Single select accessibility

Now the Select2 instance is correctly recognized as a combobox, and
the selected option is read aloud when it is focused, just like in
a standard select box.

This works by generating semi-random ids that are used for the
ARIA attributes.  These are not intended to be consistent by any
means, they are just generated to make the instance accessible by
screen readers.
Kevin Brown 10 rokov pred
rodič
commit
9ada3712f7

+ 56 - 2
dist/js/select2.amd.full.js

@@ -258,7 +258,11 @@ define('select2/results',[
     }
 
     if (data.id == null) {
-      $option.removeClass('aria-selected');
+      $option.removeAttr('aria-selected');
+    }
+
+    if (data._resultId != null) {
+      $option.attr('id', data._resultId);
     }
 
     $option.data('data', data);
@@ -363,6 +367,8 @@ define('select2/results',[
       var $next = $options.eq(nextIndex);
 
       $next.trigger('mouseenter');
+      console.log($next.offset().top, self.$results.parent().scrollTop());
+      //self.$results.parents().scrollTop($next.offset().top);
     });
 
     this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
@@ -482,13 +488,24 @@ define('select2/selection/single',[
 
   SingleSelection.prototype.render = function () {
     var $selection = $(
-      '<span class="single-select" tabindex="0">' +
+      '<span class="single-select" tabindex="0" role="combobox" ' +
+        'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
         '<span class="rendered-selection"></span>' +
       '</span>'
     );
 
     $selection.attr('title', this.$element.attr('title'));
 
+    var id = 'select2-container-';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    $selection.find('.rendered-selection').attr('id', id);
+    $selection.attr('aria-labelledby', id);
+
     this.$selection = $selection;
 
     return $selection;
@@ -510,6 +527,16 @@ define('select2/selection/single',[
       });
     });
 
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$selection.attr('aria-expanded', 'true');
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$selection.attr('aria-expanded', 'false');
+    });
+
     this.$selection.on('focus', function (evt) {
       // User focuses on the container
     });
@@ -564,6 +591,10 @@ define('select2/selection/single',[
     var formatted = this.display(selection);
 
     this.$selection.find('.rendered-selection').html(formatted);
+
+    if (data[0]._resultId != null) {
+      this.$selection.attr('aria-activedescendent', data[0]._resultId);
+    }
   };
 
   return SingleSelection;
@@ -727,6 +758,25 @@ define('select2/data/base',[
     // Can be implemented in subclasses
   };
 
+  BaseAdapter.prototype.generateResultId = function (data) {
+    var id = '';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      for (var s = 0; s < 4; s++) {
+        var idChar = Math.floor(Math.random() * 16);
+        id += idChar.toString(16);
+      }
+    }
+    return id;
+  };
+
   return BaseAdapter;
 });
 
@@ -878,6 +928,10 @@ define('select2/data/select',[
         data.children = children;
       }
 
+      if (data.id) {
+        data._resultId = this.generateResultId(data);
+      }
+
       $option.data('data', data);
     }
 

+ 56 - 2
dist/js/select2.amd.js

@@ -258,7 +258,11 @@ define('select2/results',[
     }
 
     if (data.id == null) {
-      $option.removeClass('aria-selected');
+      $option.removeAttr('aria-selected');
+    }
+
+    if (data._resultId != null) {
+      $option.attr('id', data._resultId);
     }
 
     $option.data('data', data);
@@ -363,6 +367,8 @@ define('select2/results',[
       var $next = $options.eq(nextIndex);
 
       $next.trigger('mouseenter');
+      console.log($next.offset().top, self.$results.parent().scrollTop());
+      //self.$results.parents().scrollTop($next.offset().top);
     });
 
     this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
@@ -482,13 +488,24 @@ define('select2/selection/single',[
 
   SingleSelection.prototype.render = function () {
     var $selection = $(
-      '<span class="single-select" tabindex="0">' +
+      '<span class="single-select" tabindex="0" role="combobox" ' +
+        'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
         '<span class="rendered-selection"></span>' +
       '</span>'
     );
 
     $selection.attr('title', this.$element.attr('title'));
 
+    var id = 'select2-container-';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    $selection.find('.rendered-selection').attr('id', id);
+    $selection.attr('aria-labelledby', id);
+
     this.$selection = $selection;
 
     return $selection;
@@ -510,6 +527,16 @@ define('select2/selection/single',[
       });
     });
 
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$selection.attr('aria-expanded', 'true');
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$selection.attr('aria-expanded', 'false');
+    });
+
     this.$selection.on('focus', function (evt) {
       // User focuses on the container
     });
@@ -564,6 +591,10 @@ define('select2/selection/single',[
     var formatted = this.display(selection);
 
     this.$selection.find('.rendered-selection').html(formatted);
+
+    if (data[0]._resultId != null) {
+      this.$selection.attr('aria-activedescendent', data[0]._resultId);
+    }
   };
 
   return SingleSelection;
@@ -727,6 +758,25 @@ define('select2/data/base',[
     // Can be implemented in subclasses
   };
 
+  BaseAdapter.prototype.generateResultId = function (data) {
+    var id = '';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      for (var s = 0; s < 4; s++) {
+        var idChar = Math.floor(Math.random() * 16);
+        id += idChar.toString(16);
+      }
+    }
+    return id;
+  };
+
   return BaseAdapter;
 });
 
@@ -878,6 +928,10 @@ define('select2/data/select',[
         data.children = children;
       }
 
+      if (data.id) {
+        data._resultId = this.generateResultId(data);
+      }
+
       $option.data('data', data);
     }
 

+ 56 - 2
dist/js/select2.full.js

@@ -9796,7 +9796,11 @@ define('select2/results',[
     }
 
     if (data.id == null) {
-      $option.removeClass('aria-selected');
+      $option.removeAttr('aria-selected');
+    }
+
+    if (data._resultId != null) {
+      $option.attr('id', data._resultId);
     }
 
     $option.data('data', data);
@@ -9901,6 +9905,8 @@ define('select2/results',[
       var $next = $options.eq(nextIndex);
 
       $next.trigger('mouseenter');
+      console.log($next.offset().top, self.$results.parent().scrollTop());
+      //self.$results.parents().scrollTop($next.offset().top);
     });
 
     this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
@@ -10020,13 +10026,24 @@ define('select2/selection/single',[
 
   SingleSelection.prototype.render = function () {
     var $selection = $(
-      '<span class="single-select" tabindex="0">' +
+      '<span class="single-select" tabindex="0" role="combobox" ' +
+        'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
         '<span class="rendered-selection"></span>' +
       '</span>'
     );
 
     $selection.attr('title', this.$element.attr('title'));
 
+    var id = 'select2-container-';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    $selection.find('.rendered-selection').attr('id', id);
+    $selection.attr('aria-labelledby', id);
+
     this.$selection = $selection;
 
     return $selection;
@@ -10048,6 +10065,16 @@ define('select2/selection/single',[
       });
     });
 
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$selection.attr('aria-expanded', 'true');
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$selection.attr('aria-expanded', 'false');
+    });
+
     this.$selection.on('focus', function (evt) {
       // User focuses on the container
     });
@@ -10102,6 +10129,10 @@ define('select2/selection/single',[
     var formatted = this.display(selection);
 
     this.$selection.find('.rendered-selection').html(formatted);
+
+    if (data[0]._resultId != null) {
+      this.$selection.attr('aria-activedescendent', data[0]._resultId);
+    }
   };
 
   return SingleSelection;
@@ -10265,6 +10296,25 @@ define('select2/data/base',[
     // Can be implemented in subclasses
   };
 
+  BaseAdapter.prototype.generateResultId = function (data) {
+    var id = '';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      for (var s = 0; s < 4; s++) {
+        var idChar = Math.floor(Math.random() * 16);
+        id += idChar.toString(16);
+      }
+    }
+    return id;
+  };
+
   return BaseAdapter;
 });
 
@@ -10416,6 +10466,10 @@ define('select2/data/select',[
         data.children = children;
       }
 
+      if (data.id) {
+        data._resultId = this.generateResultId(data);
+      }
+
       $option.data('data', data);
     }
 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
dist/js/select2.full.min.js


+ 56 - 2
dist/js/select2.js

@@ -687,7 +687,11 @@ define('select2/results',[
     }
 
     if (data.id == null) {
-      $option.removeClass('aria-selected');
+      $option.removeAttr('aria-selected');
+    }
+
+    if (data._resultId != null) {
+      $option.attr('id', data._resultId);
     }
 
     $option.data('data', data);
@@ -792,6 +796,8 @@ define('select2/results',[
       var $next = $options.eq(nextIndex);
 
       $next.trigger('mouseenter');
+      console.log($next.offset().top, self.$results.parent().scrollTop());
+      //self.$results.parents().scrollTop($next.offset().top);
     });
 
     this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
@@ -911,13 +917,24 @@ define('select2/selection/single',[
 
   SingleSelection.prototype.render = function () {
     var $selection = $(
-      '<span class="single-select" tabindex="0">' +
+      '<span class="single-select" tabindex="0" role="combobox" ' +
+        'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
         '<span class="rendered-selection"></span>' +
       '</span>'
     );
 
     $selection.attr('title', this.$element.attr('title'));
 
+    var id = 'select2-container-';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    $selection.find('.rendered-selection').attr('id', id);
+    $selection.attr('aria-labelledby', id);
+
     this.$selection = $selection;
 
     return $selection;
@@ -939,6 +956,16 @@ define('select2/selection/single',[
       });
     });
 
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$selection.attr('aria-expanded', 'true');
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$selection.attr('aria-expanded', 'false');
+    });
+
     this.$selection.on('focus', function (evt) {
       // User focuses on the container
     });
@@ -993,6 +1020,10 @@ define('select2/selection/single',[
     var formatted = this.display(selection);
 
     this.$selection.find('.rendered-selection').html(formatted);
+
+    if (data[0]._resultId != null) {
+      this.$selection.attr('aria-activedescendent', data[0]._resultId);
+    }
   };
 
   return SingleSelection;
@@ -1156,6 +1187,25 @@ define('select2/data/base',[
     // Can be implemented in subclasses
   };
 
+  BaseAdapter.prototype.generateResultId = function (data) {
+    var id = '';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      for (var s = 0; s < 4; s++) {
+        var idChar = Math.floor(Math.random() * 16);
+        id += idChar.toString(16);
+      }
+    }
+    return id;
+  };
+
   return BaseAdapter;
 });
 
@@ -1307,6 +1357,10 @@ define('select2/data/select',[
         data.children = children;
       }
 
+      if (data.id) {
+        data._resultId = this.generateResultId(data);
+      }
+
       $option.data('data', data);
     }
 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
dist/js/select2.min.js


+ 19 - 0
src/js/select2/data/base.js

@@ -19,5 +19,24 @@ define([
     // Can be implemented in subclasses
   };
 
+  BaseAdapter.prototype.generateResultId = function (data) {
+    var id = '';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      for (var s = 0; s < 4; s++) {
+        var idChar = Math.floor(Math.random() * 16);
+        id += idChar.toString(16);
+      }
+    }
+    return id;
+  };
+
   return BaseAdapter;
 });

+ 4 - 0
src/js/select2/data/select.js

@@ -146,6 +146,10 @@ define([
         data.children = children;
       }
 
+      if (data.id) {
+        data._resultId = this.generateResultId(data);
+      }
+
       $option.data('data', data);
     }
 

+ 7 - 1
src/js/select2/results.js

@@ -119,7 +119,11 @@ define([
     }
 
     if (data.id == null) {
-      $option.removeClass('aria-selected');
+      $option.removeAttr('aria-selected');
+    }
+
+    if (data._resultId != null) {
+      $option.attr('id', data._resultId);
     }
 
     $option.data('data', data);
@@ -224,6 +228,8 @@ define([
       var $next = $options.eq(nextIndex);
 
       $next.trigger('mouseenter');
+      console.log($next.offset().top, self.$results.parent().scrollTop());
+      //self.$results.parents().scrollTop($next.offset().top);
     });
 
     this.$results.on('mouseup', '.option[aria-selected]', function (evt) {

+ 26 - 1
src/js/select2/selection/single.js

@@ -11,13 +11,24 @@ define([
 
   SingleSelection.prototype.render = function () {
     var $selection = $(
-      '<span class="single-select" tabindex="0">' +
+      '<span class="single-select" tabindex="0" role="combobox" ' +
+        'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
         '<span class="rendered-selection"></span>' +
       '</span>'
     );
 
     $selection.attr('title', this.$element.attr('title'));
 
+    var id = 'select2-container-';
+
+    for (var i = 0; i < 4; i++) {
+      var r = Math.floor(Math.random() * 16);
+      id += r.toString(16);
+    }
+
+    $selection.find('.rendered-selection').attr('id', id);
+    $selection.attr('aria-labelledby', id);
+
     this.$selection = $selection;
 
     return $selection;
@@ -39,6 +50,16 @@ define([
       });
     });
 
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$selection.attr('aria-expanded', 'true');
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$selection.attr('aria-expanded', 'false');
+    });
+
     this.$selection.on('focus', function (evt) {
       // User focuses on the container
     });
@@ -93,6 +114,10 @@ define([
     var formatted = this.display(selection);
 
     this.$selection.find('.rendered-selection').html(formatted);
+
+    if (data[0]._resultId != null) {
+      this.$selection.attr('aria-activedescendent', data[0]._resultId);
+    }
   };
 
   return SingleSelection;

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov