CodeMirror Accessibility


CodeMirror does not currently fully support screen reader software. Since CodeMirror does not use contentEditable internally, an extra effort must be made to provide this accessibility.

Analysis of Current Functionality

CodeMirror uses a hidden textarea to accept input events, track user selections, provide copy/paste, etc. This has the benefit of some native input working correctly, but since the textarea is not a mirror of the content being entered, other things do not work.

Using NVDA, a screen reader for Windows, this is the behavior seen today with version 3.15 downloaded from http://codemirror.net/:

Possible Solution

A solution involves resetting the input value to the current line when the cursor location moves with an empty selection. Then, by calling input.setSelectionRange(from.ch, to.ch) the actual selection location is being moved, and NVDA is able to detect the changes. This value must stay in the textarea long enough for the screenreader to detect the change, but has to be cleared out quick enough for the next cursor movement to be read properly. The quick clearing out could be made unnecessary by setting the value of the textarea to CodeMirror.getValue(), however this causes some major performance bottlenecks (see below).

In addition, we do not clip the selection at any maximum limit to ensure that the selection is always consistent in the actual textarea.

Still not fixed: Entering / exiting the editor is still buggy.

Implementation Considerations

I had hoped that this could be structured as a plugin, and could avoid touching the CodeMirror internals, but that proved to not be possible. That said, it avoids large modifications to the readInput and resetInput functions, and could easily be hidden behind a property. View the changes in codemirror-accessible.js.

This still needs to be tested with more advanced CodeMirror features, like atomic markers and linked documents, and with other screen readers. Please let me know if you'd be able to help with testing these features or can give feedback with different screen readers.

Performance Considerations

The downside is that this could potentially slow things down. The selection was intentionally clipped with a low limit, and every time the selection changes, we need to set the textarea value to the current line of the CodeMirror. Performance on very long lines with CodeMirror are a known issue, and copying / selecting this data into a textarea is an extra step (though probably not significant compared to text measuring).

Originally, I was setting the textarea's value to the entire content (which seems to be best for the screenreader), but there is a major impact is caused by the computation of the selectionRange in this case. Textareas setSelectionRange method takes an absolute start and end value, while CodeMirror operates on individual lines. So we need to count from 0 to currently selected line and add the length of each line to arrive there.

Demos

This is working in the demo below. Compare the first CodeMirror (with the fix applied), the second CodeMirror (using the original version), and a normal textarea (at the bottom).

You can also see a video of the three options here:


makes it easier to debug and see what is going on

CodeMirror With Accessibility Fix direct link


CodeMirror Original direct link

Normal Textarea direct link