Web без мышки

в 12:49, , рубрики: accessibility, hotkeys, usability, UX, WCAG, Блог компании NetСracker, Веб-разработка

Web без мышки - 1 Наверное, все при взгляде на этот экран мысленно переносят обе руки на клавиатуру. Да, тут можно было навигироваться без мышки и это было быстро и хорошо! Многие до сих пор используют подобные менеджеры (Total commander, Far etc).

С другой стороны, почти на всех современных сайтах, порталах и решениях, построенных для веба, пользователь вынужден постоянно отрывать руки от клавиатуры, целиться мышкой в кнопку/иконку/поле, а затем опять возвращать руки на клавиатуру для ввода текста.

Как же достичь удобства навигации без мышки в вебе?

Большая часть продуктов, которые разрабатывает и внедряет наша компания, выходит на конечного пользователя. Операторы колл-центров, инженеры, менеджеры – все они работают с пользовательским интерфейсом «для внутреннего использования» – B2B интерфейсами. Возможность работы с системой с клавиатуры без использования мыши – важное требование в B2B интерфейсах. Почему?

  • В первую очередь – скорость работы. Пользователю не нужно изменять положение рук и переключаться на мышку, напротив, у него есть мгновенный доступ к функциям.
  • Пользователю сложнее сделать ошибку (нужно целиться в кнопку, а не в пиксель).
  • Поддержка людей с ограниченными возможностями.
  • Снижение нагрузки на глаза (многие функции можно выполнять вообще не смотря на монитор).

Такая навигация может выглядеть не «секси» и требовать больше времени на обучение, но бонусы от использования значительно превышают эти минусы.

Итак, задача

Есть большое количество web-based пользовательских интерфейсов. Необходимо:

  • с минимумом усилий позволить использовать весь функционал наших решений без мыши
  • иметь возможность быстро адаптировать любые новые страницы для использования без мыши
  • иметь возможность тонкой настройки навигации по АРМам для увеличения эффективности работы всех пользователей
  • все это с минимальным воздействием на существующий код

Анализируем

Существует четыре основных подхода к навигации клавиатурой, рассмотрим их:

  • табуляция (tab)
  • хоткеи (hotkeys)
  • пространственная навигация (spatial)
  • каретка (caret)
Табуляция

Табуляция — переход между элементами интерфейса с помощью клавиши tab (shift+tab).

  • + поддержка по умолчанию во всех популярных браузерах
  • − всего два направления (+shift)
  • − ограниченные возможности конфигурации (ring)...
Горячие клавиши

Горячие клавиши (в просторечии хоткеи) — сочетание клавиш, которое вызывает определённую функцию. Это позволяет упростить доступ к основным функциям системы. Всё больше сайтов начинают использовать хоткеи на свои страницах для доступа к самым востребованным функциям, среди них: Habrahabr, яндекс, гугл почта и другие. Но что делать, если функций много, и на каждой странице разные? Просто невозможно будет запомнить все хоткеи, а значит, использование не будет эффективным. Так же есть проблема контекста: когда на странице несколько таблиц и несколько кнопок save, например.

  • + мгновенный вызов любой функции
  • − требует обучения (запоминания сочетаний клавиш)
  • − при большом количестве функций сложно запомнить сочетания (особенно если несколько страниц как у нас)

Пример реализации: расширения для Firefox используют подход с hotkeys, очень распространены в десктоп приложениях (пример — аутлук).

  • + не нужно менять существующий код
  • − только браузер Firefox
  • − несколько кнопок save
  • − никакой семантики, такие хоткеи сложно запомнить
  • − на разных страницах одни и те же действия могут иметь разные хоткеи
Пространственная навигация

Когда элементы на веб страницах стали позиционировать с помощью css, навигация табом перестала справляться со своей задачей: курсор перескакивал по элементам дизайна в порядке их объявления в html документе, а не в том, в каком их видит пользователь. Тогда некоторые браузеры (Firefox, Opera) реализовали пространственную навигацию. Они позволяли пользователям использовать сочетания shift+стрелки для перемещения между элементами дизайна, причём следующий элемент определялся исходя из его фактического расположения на экране.

  • + позволяет навигироваться по незнакомому интерфейсу
  • + поддержка OOB в некоторых браузерах
  • − поддержка не во всех браузерах
  • − там, где поддержка есть, она включается опционально
  • − нет стандарта сочетаний клавиш для переходов
Перемещение каретки

Особенность этого подхода в том, что курсор пользователя перемещается не только по элементам форм и ссылкам, а по всему содержимому страницы (как в ворде). Перемещаться можно, как и в пространственном подходе, в четырёх направлениях.

  • + позволяет выделять, копировать фрагменты текста
  • − сфокусирован на контенте, а не элементах управления
  • − медленнее, чем пространственная навигация

Вышеприведенные решения as is нам не подходят из-за отсутствия поддержки во всех браузерах, в них отсутствует возможность тонкой настройки для конкретных страниц, а также хотелось бы, чтобы и хоткеи и пространственная навигация настраивались единообразно в одном месте.

Представляем Mouseless

Web без мышки - 2

Ключевыми особенностями Mouseless являются:

  • простая конфигурация для всех страниц приложения, используя CSS селекторы
  • работа во всех браузерах
  • возможность тонкой настройки
  • не конфликтует с существующими решениями
  • хоткеи и пространственная навигация в одном флаконе
Принцип работы Mouseless

Страница делится на блоки с помощью привычных CSS селекторов. Блоки могут иметь дочерние блоки. Внутри каждого блока определяется свой набор хоткеев (JSON объект). Хоткеи могут наследоваться внутренними блоками, т.е. у хоткеев появляется область видимости, которым можно управлять (например, хоткей для сохранения один и тот же в разных блоках, но работает по-разному в зависимости от текущего блока).

Web без мышки - 3
На рисунке показано разделение на блоки и подблоки.

Конфигурация каждого блока представляет из себя json объект, json объекты для всех блоков образуют конфигурацию страницы.

Пространственная навигация в данном случае является частным случаем хоткеев. Сводим к минимуму кол-во обязательных параметров конфигурации, базовая поддержка должна быть доступна с минимумом действий. Простейшая конфигурация:

new MouselessBlock({  //объявление блока
	selector: "#blockId", // селектор задаёт границы блока
	childSelector: “a”, // селектор определяет элементы на которые будет перемещаться фокус
	keys: [ // массив хранит все хоткеи, используемые в данном блоке
		new MouselessAction({key: ncKey.KEY_LEFT, action: ncKey.gotoPrevElem }), //по нажатию на клавишу влево выполнить переход на предыдущий элемент
		new MouselessAction({key: ncKey.KEY_RIGHT, action: ncKey.gotoNextElem })
	]
});

Внутри блока #blockId можно перемещать фокус между ссылками клавишами влево и вправо. gotoPrevElem/gotoNextElem служебные функции, можно так же использовать свои кастомные функции.

На реальных кейсах расширяем базовую библиотеку:

  • кольца. Чтобы навигироваться по списку вновь и вновь по кругу, нужно в блок добавить параметр ring: true
  • элементы блока, получающие фокус по умолчанию: возможность указать элемент, который первым получает фокус при попадании в блок defaultChildIndex:2
  • поддержка пользовательских функций: можно установить свою собственную функцию, которая будет вызвана по нажатию клавиш. new MouselessAction({key: ncKey.KEY_LEFT, action: customFunction })
  • работа с диалоговыми окнами (попапами): модальные окна работают в отдельном контексте, их конфигурация осуществляется независимо от основной
    ncKey.addBaseConf(parentSelector, blocks); //добавление блоков в основную конфигурацию
    ncKey.addCustomConf(parentSelector, blocks); //добавление блоков в конфигурацию динамически подключаемых модальных блоков
    //parentSelector - селектор родительского блока, если "" - добавление в корень конфигурации.
    //blocks - массив блоков для добавления
    
  • сохранение/восстановление фокуса применяется в основном совместно с модальными окнами для приведения фокуса в состояние до открытия окна
    var curFocus = ncKey.saveFocus(); 
    …
    ncKey.restoreFocus(curFocus);
    
  • наследование ncKeyAction'ов, поддержка глобальных хоткеев: например, на странице есть несколько таблиц, у каждой кнопка save. Хотим, чтобы в любой ячейке таблицы можно было выполнить сохранение по хоткею (частный случай — глобальные хоткеи). Т.е. ncKeyAction, назначенные в родительском блоке, будут работать во всех дочерних:
    ncKey.addCustomConf("", [new MouselessBlock({//блок, описывающий попап
            selector:"#popup-window",
            childSelector: ncKey.FOCUSABLE_SELECTOR,
            keys: [new MouselessAction({key: ncKey.KEY_ESCAPE,  action: closePopup}), 
                   new MouselessAction({key: ncKey.KEY_S,  action: saveAndClosePopup})
            ],
            childBlocks: [new MouselessBlock({ //дочерний блок с контентом
                    selector:"#popup-window .body",
                    childSelector: ncKey.FOCUSABLE_SELECTOR,
                    ring: false,
                    keys: [...]
                }),
                new MouselessBlock({ //дочерний блок с кнопками
                    selector:"#popup-window .btns",
                    childSelector: ncKey.FOCUSABLE_SELECTOR,
                    ring: false,
                    keys: [...]
               })
    		]
    	})
    ]);
    

    Закрытие попапа с сохранением и без будет работать во всех блоках.

Конфигурация блока на примере NavigationTree (раскрывающее дерево):

new MouselessBlock({ 
selector: "#navigationTree",
childSelector: "li > a:visible",
ring: true,
keys: [ new MouselessAction({key: ncKey.KEY_LEFT, action: ncKey.gotoPrevBlock, ctrl: true}),
           new MouselessAction({key: ncKey.KEY_RIGHT, action: ncKey.gotoNextBlock, ctrl: true}),
           new MouselessAction({key: ncKey.KEY_UP, action: ncKey.gotoPrevElem, ctrl: false}),
           new MouselessAction({key: ncKey.KEY_DOWN, action: ncKey.gotoNextElem, ctrl: false}), 
           new MouselessAction({key: ncKey.KEY_RIGHT, action: openNavTreeNode, ctrl: false}), 
           new MouselessAction({key: ncKey.KEY_LEFT, action: closeNavTreeNode, ctrl: false}) 
]});

Теперь можно ходить по дереву стрелками вверх-вниз, открывать-закрывать ветки влево-вправо. Функции openNavTreeNode/closeNavTreeNode были написаны до внедрения Mouseless (были опубликованы как api к дереву).

Таким образом, даже в этом случае не пришлось писать новый код, обходимся простой конфигурацией.
CSS легко заменяется под любую тему, достаточно описать правила для подсветки активируемых элементов и активного. Можно добавить свои, более сложные, для конкретных блоков или элементов.

Итого

Внедрив Mouseless в наши решения мы получили библиотеку, которая позволяет обеспечить быструю базовую поддержку навигации с клавиатуры, с одной стороны, и глубокую настройку для достижения максимальной эффективности и удобства использования, с другой.

Дополнительным бонусом стало то, что мы покрыли часть рекомендаций W3C “Web Content Accessibility Guidelines” ( www.w3.org/TR/WCAG20 ).

Работа в этом направлении не закончена, будем продолжать, ждём реакцию сообщества.

Автор: NetСracker

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js