Accessible Images Using Angular

Posted on:

Angular.js is becoming a widely used framework for web development. The framework allows developers to create readable code, is known to be fully extensible, and works well with other libraries. However, developers must still ensure accessibility when using the angular framework.

While angular has accessibility features using the ngAria module, there are basic accessibility techniques that still need to be considered. Unfortunately, the tutorials in the Angularjs.org site lack basic accessibility. For example, the Angular.js tutorial covers two examples using images. The first covers basic images and displaying images on the page. The second takes these images and makes them actionable elements but accessibility is not addressed. In this blog post, I will talk about using images through angular and ensuring images are accessible by adding alternative text, can be accessed using a keyboard, and proper ARIA roles are set.

Adding Alt Text to an Image

In Step 6 of the Angular.js Tutorial (Templating Links & Images), the image example template lacks the basic and necessary alt attribute. However, this can easily be implemented in this step. In the following referenced JSON file example, there is already an array with a friendly name to use as our alt text.

JSON snippet

[
 {
 ...
 "id": "motorola-defy-with-motoblur",
 "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
 "name": "Motorola DEFYu2122 with MOTOBLURu2122",
 ...
 },
 ...
 ]

Original Non-compliant Angular Template Example without alt text

<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
…
</li>
</ul>

Developers can easily add alt=”{{phone.name}}” to use as alternative text. This captures the name from the existing angular snippet.

Updated Compliant Example

<ul class="phones">
  <li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
   <a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}" alt=”{{phone.name}}”></a>
    …
 </li>
</ul>

Another Compliant example of using alt text

Using two logo images, developers can easily incorporate angular into standard HTML elements as shown in the following example:

Angular snippet

app.controller('SsbImageController', function(){
 this.products=images;
 this.heading2="Image Examples";
 });
var images= [
 {
 src:'http://www.ssbbartgroup.com/wp-content/themes/SSBBart2014/images/logo.png',
 alt:'SSB BART Group, Inc.',
 pageUrl:'http://www.ssbbartgroup.com',
 name:'ssb',
 },
 {
 src:'https://www.google.com/images/srpr/logo11w.png',
 alt:"Google, Inc.",
 pageUrl:'http://www.google.com',
 name:'google',
 },

HTML Snippet

<div ng-controller="SsbImageController as ssb">
<h2>{{ssb.heading2}}</h2>
 <div ng-repeat="images in ssb.products">
   <img ng-src="{{images.src}}" alt="{{images.alt}}"/>
 </div>
</div>

Custom Image Controls

In Step 10 of the Angular.js Tutorial (Event Handlers), these images become actionable without using a surrounding anchor element. In addition to missing alternative text, these lack proper roles and keyboard interaction. The image includes an ngClick directive, which when used in standard actionable controls, also takes on a keypress event. However, this does not work for custom controls that are not actionable by default.

Using a similar compliant example in the previous issue, we captured a single image. We used a custom directive and added a role, tabindex, and a redundant keypress event handler mapped to the enter key and spacebar. The role will allow screen readers to identify the image as a link. The tabindex and keypress event handler will allow keyboard only users to navigate to and activate this “image link.” The redundant keypress event can be binded in the custom directive or can be accomplished using the ngKeypress directive.

Non-compliant Example

Angular snippet

phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', '$http',
 function($scope, $routeParams, $http) {
 $http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
 $scope.phone = data;
 $scope.mainImageUrl = data.images[0];
 });

$scope.setImage = function(imageUrl) {
 $scope.mainImageUrl = imageUrl;
 };
 }]);

HTML snippet

<ul class="phone-thumbs">
 <li ng-repeat="img in phone.images">
  <img ng-src="{{img}}" ng-click="setImage(img)">
 </li>
</ul>

Images.js file

(function() {
 'use s+trict';
 var app= angular.module('ssbExamples', []);

app.controller('SsbImageController', ['$scope',
 function($scope) {
 this.products=images;
 this.heading2="Image Examples";
 }]);

app.controller('CustomImageController',['$scope',
 function($scope) {
 this.products=images;
 this.heading2="Custom Control Image Link";
$scope.setImage = function(images) {
 setURL();
 };

/*************
 Option #1:
 Can also be accomplished using custom directive and binding the keypress event
 *************/
 //redundant to ngClick to support spacebar and enter key
 $scope.setImagesKeyed = function(e){
 if ((e.which==13) || (e.which==32)){
 setURL();
 e.preventDefault();
 }
 };
 }]);

app.directive("customLink", ['$parse', function($parse) {
 return {
 compile: function(tElm,tAttrs){
 return function (scope,elem){
 elem.attr('tabindex', '0');
 elem.attr('role', 'link');
/*************
 Option #2:
 Can also be accomplished using ngKeypress directive
 *************/
 //redundant to ngClick to support spacebar and enter key
 //elem.bind('keypress', function(e){
 // if ((e.which==13) || (e.which==32)){
 // setURL();
 // e.preventDefault();
 // }
 //});
 };
 }
 };
 }]);

function setURL(){
 var locale= "http://www.ssbbartgroup.com";
 window.location.href = locale;
 }

var images= [
 {
 src:'http://www.ssbbartgroup.com/wp-content/themes/SSBBart2014/images/logo.png',
 alt:'SSB BART Group, Inc.',
 pageUrl:'http://www.ssbbartgroup.com',
 name:'ssb',
 },
 {
 src:'https://www.google.com/images/srpr/logo11w.png',
 alt:"Google, Inc.",
 pageUrl:'http://www.google.com',
 name:'google',
 },
 ];
 })();

HTML snippet

<!--
Option #1 Using a ngKeypress directive
-->
<div ng-controller="CustomImageController as custom">
  <h2>{{custom.heading2}}</h2>
  <div ng-repeat="images in custom.products | filter: {name:'ssb'}">
   <img ng-click="setImage(images)" ng-keypress="setImagesKeyed($event)" ng-src="{{images.src}}" alt="{{images.alt}}" custom-link />
 </div>
</div>

<!--
Option #2 Binding the keypress event in the customLink directive.
-->
<div ng-controller="CustomImageController as custom">
  <h2>{{custom.heading2}}</h2>
  <div ng-repeat="images in custom.products | filter: {name:'ssb'}">
   <img ng-click="setImage(images)" ng-keypress="setImagesKeyed($event)" ng-src="{{images.src}}" alt="{{images.alt}}" custom-link />
  </div>
</div>

Summary

It is straightforward to add accessibility into apps created with Angular. As with many programming languages, there are multiple ways of achieving the same goal. Personal coding styles vary for each developer. The goal of this post is to provide an example which includes accessibility features into the angular.js framework and demonstrate that coding such features does not have to be a complicated feat. Ultimately it would be best to have angularjs.org’s examples updated to include alt text so that developers copying these examples would automatically be gaining accessibility features and could learn how to write accessible Angular code from the same source that they use to learn Angular.

Happy Coding!

« More Blog Posts

Put yourself on the path to complete accessibility!

Just fill out the form below and we’ll contact you to set up your free consultation with an expert specializing in your industry.

If you need more immediate assistance, please send an email to info@ssbbartgroup.com or call (800) 889-9659. We look forward to helping you!