Tag Archives: a11y

Building An Accessible Drag & Drop User Interface for Images or Text

See the Pen Accessible Draggable UI by Lisa Folkerson (@lisafolkerson) on CodePen.

Building Accessibility

Web Accessibility Initiative’s Accessible Rich Internet Applications (WAI-ARIA) allow us to take web sites, or web apps and take the interactive widgets that are not self-evident to a screen reader and tag them meaningfully so that assistive technology can interpret the widget and its behavior and potential behavior properly for its user.

If you are interested in finding out more try out these amazing resources:

https://dev.opera.com/articles/introduction-to-wai-aria/
http://www.w3.org/TR/wai-aria-practices/#dragdrop
https://dev.opera.com/articles/accessible-drag-and-drop/

A lot of accessibility info is out of date and/or dry. Dense docs are full of information but short on use-cases. Googling implementation of different accessibility standards can be difficult as well; with examples being sometimes out-of-date or using endless lines of uncommented code. The above versions are great. My version has 52 lines which I will describe in detail.

Dependencies being used include .normalize, jQuery UI 1.11.2 (including the jQuery UI stylesheet), jQuery 2.1.3, and animate.css. I wrote the codepen in jade but converted the markup to html here for simplicity, similarily the code on the codepen is in Sass but I have included CSS for clarity.

This tutorial shows you how to take several different pieces of content, be they text or images and load a larger version in a main dropZone using jQuery UI. For this example I used text that are images. If you want to talk about how you would change the code for a different kind of content or event, leave a comment. If you want to look at more of these characters check out http://copypastecharacter.com/ The counting is less important  but kind of nice. I don’t go into detail on how its done but if anything is unclear please leave a comment.

Using jQuery UI’s built-in drag and drop

Draggabble

Draggable items are defined in jQuery. Making sure you are loading jQuery and jQuery UI, first define a document ready function.

$(function () {
 });

What I did next is make a global variable for each item that would be draggable as well as the parent div.

var $gallery = $('.boxes'),
 $box = $('.box');

Use the .draggable() method for those divs with the box class. Inside we specify options to define what kind of draggable they should be.

$box.draggable({
 revert: false,
 helper: 'clone',
 scroll: true
 });

The revert option takes the boolean true or false and controls whether the div will return to its original position or not.
helper accepts original | clone | fn() options and specifies whether the draggable object will the object itself, a clone of it, or something unique written in a function. scroll: true allows for scrolling while dragging the object.

There are many other options to get the draggable gallery you want at https://jqueryui.com/draggable

Droppable

After draggable elements are coded the next step is to specify the targets they can be dragged to. Define a variable for this location:

var $dropZone = $('.dropZone');

Using the .droppable() method gives us a number of options to define as well. Find the full list at the docs.

$dropZone.droppable({
    activeClass: 'focus-state',
    hoverClass: 'ui-state-hover',
    drop: function(event, ui) {
    $( this ).find( '.changingText' ).text( ui.draggable.text() ).removeClass( 'change ').addClass('tada animated changingText' ).appendTo (this ); }
});

The activeClass and hoverClass can load CSS classes from jQuery UI stylesheet. If you don’t want to use these out of the box, feel free to define your own. Here, ‘ui-state-hover’ is from jQuery UI stylesheet, and ‘focus-state’ is a class I have defined in my own stylesheet.

The drop option sets a function to run on drop. This one is a pretty long chain. While you can run any function at this point what I have running says In here find the text with the class .changingText, make it the text ui.draggable.text() (which the .draggable() method adds for you), remove the class of .change, add the classes changingText, tada and animated ( tada and animated are from animate.css and changingText is the large text that changes over and over again), and add it back into here.

Using JavaScript and jQuery to supply and update WAI-ARIA information

Aria-Grabbed

Aria-grabbed values tell a screen reader if an object has the potential to be dragged, is being dragged, or if it cannot be dragged.

The three states aria-grabbed will accept are false | true | undefined

 <div class="box" aria-grabbed="false">

Anything that has the potential to be dragged should be set to false. Then we’ll use jQuery to update the attribute during the drag event. Inside the .draggable() function use drag event to listen for a function that uses jQuery to set the state of true for the aria-grabbed value:

drag: function () {
        $(this).attr('aria-grabbed', 'true');
    },

Clean up after the drag event has occurred by resetting the aria-grabbed state to false or undefined(If you would like the drag to only be executable once).

stop: function () {
      $(this).attr('aria-grabbed', 'false');
  }

Aria-dropEffect

Aria-dropEffect values let the user know if the object currently being dragged can be dropped in that location, and what will happen in that instance.

 dropZone" aria-dropEffect="none">

Aria-dropEffect attribute can take the values copy | move | reference | execute | popup | none When there is nothing currently being dragged as well as on pageload the div from the dropZone should be set to ‘none’.

However, once something is being dragged potential targets should be defined and updated using jQuery. We set these as options in the .draggable method. And define functions to run both as content is being dragged, and to execute at the end of the drag event.

 
$box.draggable({
      revert: false,
      helper: 'clone',
      scroll: true,
      drag: function(){
        $( this ).attr( 'aria-grabbed', 'true' );
        $dropZone.attr( 'aria-dropEffect', 'execute' );
    },
      stop: function(){
        $( this ).attr( 'aria-grabbed', 'false' );
        $dropZone.attr( 'aria-dropEffect', 'none' );
    }
  });

On drag I’m running a function that changes aria-grabbed to true for the specific div being dragged, and changes its dropZone to have the attribute #[code execute] for aria-dropEffect, letting the user know that a function will execute (aka the image/text will update) if the content is dropped in that location.

On stop the DOM is tidied and I return elements affected in the drag to their resting values.

Using the keyboard only

Making Divs Tabbable

Being accessible means that all the operations of a page or web app are operable through the keyboard alone.

Tabbing through a basic site will navigate through each element that is focusable (ie. any link or form element) in the order they appear in the markup. This excludes divs. Tabbing can be re-written/overwritten using the attribute tabindex=0 on each of the divs. So now the markup for each div containing the content we want draggable is:

 <div class="box" aria-grabbed="false" tabindex="0">

Submitting tabbable

Now that the divs containing content that is draggable can be tabbed through, we need a way to submit them using the only the keyboard. For this we write a lil JS to execute padding the information to the dropZone on enter.

$box.bind('keyup', function(e) {
      e.preventDefault();
      var elem = $( this )[0];

      if ( e.keyCode === 13 ) { // enter
        $( '.changingText' ).remove();
        $( elem ).find( 'p' ).clone().appendTo( '.dropZone' ).addClass( 'changingText tada animated' ).removeClass( 'change' );
    }
});

We bind the key event to the box focused on when the event occurs, then we run the function.

The key function takes one parameter, e which is used in the first line inside the function to prevent the default of the key. After that we define a variable that we can use to access the #[code p] information since this is referring to an array, we reach inside and grab object 0.

Inside the key function is an if statement. If the key pressed is enter (keycode 13), remove the text with the class of changingText. Then, find the p inside the object we’re in, copy it, add it to the dropZone, give it the classes that make it dance and maintain the DOM structure by adding the changingText class and removing the change class.

Navigating Tabbable Elements with Arrow Keys

Alright. So now you can tab through the draggable elements in one order. It makes sense, though, to allow your user to go back and forth in this case. So in the keyup function we can add useability. Because the elements here are laid out inline it is intuitive to be able to use the left and right arrow keys. So, still in the function that says ‘when a key is pressed while a .box is focused’ we extend the if statement. IF enter, send the content to the dropzone, OR ELSE IF left arrow is pressed focus on the preceeding element, OR ELSE IF the right arrow is pressed focus on the next element.

$box.bind('keyup', function(e) {
  e.preventDefault();
  var elem = $( this )[0];

if ( e.keyCode === 13 ) { // enter
    $( '.changingText' ).remove();
    $( elem ).find( 'p' ).clone().appendTo( '.dropZone' ).addClass( 'changingText tada animated' ).removeClass( 'change' );
} else if( e.which === 37) { //left arrow
    $( '.box:focus' ).prev().focus();
} else if( e.which === 39 ) { //right arrow
    $( '.box:focus' ).next().focus();
  }
}); // end keyup event

The Markup

In the end the markup is 28 lines of code. My example also counts the number of times the drag and drop is successfully run.

The CSS

The CSS is the bulkiest part of the code. When considering accessibility it’s also important to remember to have elements that are high-contrast, code that is large enough to read with ample line-height, and descriptive visual clues for people who are sight-impaired or otherwise aided by text being legible (everyone to some extent or another). CSS is viewable on codepen or downloadable below.

The JS

The JavaScript is pretty concise. Coming in at 52 lines including comments. This is because jQuery and jQuery UI and are very user-friendly and well-documented.

How it all comes together

The code runs in Chrome, Firefox, Safari, and IE9+

If you want to download the code, that is HERE

Thank you to thebillygregory for resources and kindness.