![](https://habrastorage.org/files/b48/9bc/a89/b489bca89b864a0cbc59ac92dc21dad3.png)
С другой стороны, почти на всех современных сайтах, порталах и решениях, построенных для веба, пользователь вынужден постоянно отрывать руки от клавиатуры, целиться мышкой в кнопку/иконку/поле, а затем опять возвращать руки на клавиатуру для ввода текста.
Как же достичь удобства навигации без мышки в вебе?
Большая часть продуктов, которые разрабатывает и внедряет наша компания, выходит на конечного пользователя. Операторы колл-центров, инженеры, менеджеры – все они работают с пользовательским интерфейсом «для внутреннего использования» – 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
![](https://habrastorage.org/files/29e/b85/605/29eb856050364e7599efb4390ac28336.gif)
Ключевыми особенностями Mouseless являются:
- простая конфигурация для всех страниц приложения, используя CSS селекторы
- работа во всех браузерах
- возможность тонкой настройки
- не конфликтует с существующими решениями
- хоткеи и пространственная навигация в одном флаконе
Принцип работы Mouseless
Страница делится на блоки с помощью привычных CSS селекторов. Блоки могут иметь дочерние блоки. Внутри каждого блока определяется свой набор хоткеев (JSON объект). Хоткеи могут наследоваться внутренними блоками, т.е. у хоткеев появляется область видимости, которым можно управлять (например, хоткей для сохранения один и тот же в разных блоках, но работает по-разному в зависимости от текущего блока).
![](https://habrastorage.org/files/d96/e05/43d/d96e0543ddce47928f2dd6a75274ea8f.png)
На рисунке показано разделение на блоки и подблоки.
Конфигурация каждого блока представляет из себя 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 ).
Работа в этом направлении не закончена, будем продолжать, ждём реакцию сообщества.