Ext.FocusManager
Куда подевался Ext.FocusManager?
Как теперь понять, на чем фокус стоит? Как отслеживать перемещение фокуса? |
Ext.FocusManager убит, расчленён, проткнут осиновым колом в его чёрное сердце, выжжен напалмом, пепел захоронен под Чернобыльским саркофагом, и имя его забыто. Я это сделал собственноручно и с превеликим удовольствием, поэтому как доктор говорю.
Как понять, на чём фокус стоит: // всегда было var focusedElement = document.activeElement; // Вернёт то же самое, если вам так удобнее focusedElement = Ext.Element.getActiveElement(); // Вернёт компонент, которому принадлежит сфокусированный элемент // Легко может оказаться null var focusedComponent = Ext.ComponentManager.getActiveComponent(); Как отслеживать перемещение фокуса... Вот это вопрос на миллион долларов, с радиоактивными тентаклями которого я бодаюсь уже четвёртый год. В тривиальном случае:
component.on('focusenter', function() {
console.log('компонент сфокусирован');
});
component.on('focusleave', function() {
console.log('компонент потерял фокус');
});
Очень надеюсь, что менее тривиальные случаи вас не побеспокоят. ;) |
focusenter мне не поможет отследить фокус.
Потому что component не известен. И собственно отслеживание фокуса нужно как раз для того, чтобы узнать component, на котором фокус. Вот это Ext.ComponentManager.getActiveComponent(); конечно уже лучше. Но как отследить изменение фокуса? Хотя можно по идее делать по цепочке. 1) узнать текущий фокус Ext.ComponentManager.getActiveComponent(); 2) подписаться на focusleave 3) как только происходит focusleave тут же перейти в пункт 1. Муторный способ однако... |
Объясните подробнее, чего хотите добиться.
|
Хочу отслеживать фокус. Например, при смене фокуса, чтобы в консоли появлялось сообщение: фокус изменен с компонента1 на компонент2
|
Отслеживание фокуса само по себе особого смысла не имеет, только если из праздного любопытства. Скорее всего вам это нужно для каких-то конкретных целей, например валидирования данных в форме или ещё чего-нибудь. Вот это я и пытаюсь выяснить: какова ваша задача, в чём цель отслеживания фокуса?
Это я не педантизма ради, просто accessibility в целом и управление фокусом в частности это как раз одно из моих основных направлений работы в Sencha. Я могу вам подсказать, как сделать то или это, но мне нужно понимать, чего вы хотите добиться, чтобы посоветовать оптимальный вариант. |
Вы наверное все секреты можете выведать. Да, мне нужно прикрутить чертов сканер штрих-кодов к браузеру. Таблицы grid к сожалению не пропускают пробелы и Enter-ы. Вот сижу и думаю как бы эту неприятность обойти.
|
Мне ваши секреты ни к чему, правда. :) Но я рад, что мы постепенно продвигаемся к цели.
Grid не "не пропускает" пробелы и Enter, эти клавиши имеют специальное значение. Пробел выделяет строку или ячейку, Enter переводит таблицу из навигационного режима (Navigable mode) в активизационный режим (Actionable mode). Названия и поведение таблицы взяты прямиком отсюда: https://www.w3.org/TR/wai-aria-practices/#grid. Изменять это поведение в общем случае я бы не рекомендовал, но если есть чёткое понимание, как UI должен работать, то можно сделать исключение. Хотя и непонятно пока, зачем. Вам нужно со сканера данные получать и вставлять в таблицу? В этом случае можно не обращать внимание на фокус и просто добавлять записи в Store. Grid знает, что нужно делать, когда в нём сфокусирована ячейка и надо обновить таблицу. Или задача в чём-то ещё? |
Сканер отправляет строку с префиксом и постфиксом. В качестве постфикса используется код 13 Enter. И если случайно (пользователь все может) фокус будет на каком-либо гриде, то 13-й код не пройдет и штрих-код не будет считан (точнее его конец не будет считан).
У меня работа со сканером глобальная. То есть не привязана ни к какому элементу. Поэтому пользователю не требуется перед сканированием указывать фокус (то есть специально выбирать элемент, куда будет произведен ввод со сканера). |
А какой префикс? И вообще, какие символы приходят со сканера и сколько?
|
$<цифры-буквы><13>
С баксом проблем нет, грид его пропускает. И все остальное тоже, кроме 13. . |
Цитата:
Цитата:
Пробовал найти такую опцию в классах: Ext.view.AbstractView Ext.view.View Ext.panel.Table Ext.grid.Panel . |
Цитата:
Хм. Если подумать, то задачка-то забавно нетривиальная даже без учёта вопроса с браузерным окном. Отслеживание активного компонента тут слабо поможет, особенно если вашему приложению нужно работать в IE. Я бы пошёл другим путём: можно установить обработчик события keydown на уровне документа со срабатыванием в фазе перехвата, и при опознавании префикса "подставлять" под следующие за ним "нажатия клавиш" некое специальное поле. Примерно так:
Ext.define('My.app.Application', {
extend: 'Ext.app.Application',
launch: function() {
this.scannerField = new Ext.form.field.Text({
renderTo: Ext.getBody(),
floating: true,
hidden: true,
focusOnToFront: true,
cls: 'x-hidden-clip',
shadow: false,
listeners: {
specialkey: function(field, e) {
if (e.getKey() === e.ENTER) {
e.preventDefault();
e.stopEvent();
// Это просто чтобы не заморачиваться с разрешением
// scope через references в этом примере
Ext.app.Application.instance.fireEvent('scanner_input', this.getValue());
this.setRawValue('');
this.hide();
}
}
}
});
Ext.getDoc().on('keydown', function(e) {
// 52 это код клавиши для '$'
if (e.getKey() === 52) {
e.preventDefault();
e.stopEvent();
Ext.app.Application.instance.scannerField.show();
return false;
}
}, { capture: true });
}
});
Это решение не на 100% свободно от побочных эффектов, но в вашем случае такого решения скорее всего вообще нет. Для пущего спокойствия душевного, я бы проделал серию бесчеловечных экспериментов над живым сканером; главный вопрос это насколько быстро случается "ввод", т.е. каков разрыв по времени между префиксом и постфиксом. Я очень сильно подозреваю, что разрыв близок к 0 мс, но могут быть варианты. Если разрыв минимален, то вышеописанное решение должно иметь приемлемую надёжность. Я протестировал по-быстрому в Chrome, но по идее в IE тоже должно сработать. Учтите также, что несмотря на кажущуюся простоту кода, механизмы здесь задействуются сложные и колдунство вельми сильное. Если нужно что-нибудь поменять, лучше сперва спросите. Я с удовольствием объясню, что и как, но это может оказаться надолго. :) |
Цитата:
Цитата:
Здесь я ничего не нашел: http://docs.sencha.com/extjs/6.0/6.0...iner-method-on Цитата:
2) И еще вопрос: А зачем после $ надо показывать поле Ext.app.Application.instance.scannerField.show(); а потом после 13 - скрывать? Из-за этой опции focusOnToFront: true? Хитро однако... . |
Цитата:
Цитата:
Цитата:
Цитата:
Поэтому поле сделано невидимым, но фокусируемым, с помощью CSS правила clip: rect(0,0,0,0), которое применяется стилем 'x-hidden-clip'. Остальные хитрости как раз для автомагического управления фокусом: поле у нас "плавающий" компонент с абсолютным позиционированием в теле документа, поэтому в раскладках не участвует и на другие компоненты не влияет. Когда мы это поле показываем через scannerField.show(), глобальный ZIndexManager сортирует свою коллекцию плавучих компонентов и выпихивает наверх наше поле, попутно его фокусируя. Поле, оказываясь наверху стека и получая фокус, запоминает предыдущий сфокусированный элемент и отпуливает фокус обратно, когда мы это поле скрываем. Флаг focusOnToFront должен быть true по умолчанию для всех плавучек, но я просто подстраховался, т.к. некоторые классы его меняют и я не помню точно, какие именно. Примерно так, если без нюансов. :) А ещё если подумать, то надо очистку поля через setRawValue('') передвинуть из обработчика specialkey в глобальный перехватчик. И вопрос о длительности интервала между $ и Enter был вовсе не праздным, промышленную версию этого решения неплохо бы обвешать предохранителями на тему ложных срабатываний. Как говорил незабвенный поручик, случаи разные бывают... |
Цитата:
Это для задач, где пользователь не решает, какой браузер ему использовать. Цитата:
Я попробовал в действии опцию { capture: true }. Для keydown она работает, грид не может перехватить и заблокировать событие, а вот для keypress эта опция не сработала, грид заблокировал событие, почему такая разница??? В итоге, мне в общем-то достаточно опции capture, чтобы решить проблему, разве что событие keypress придется поменять на keydown. Но, единственный недостаток решения это то, что capture это недокументированная опция, надо заметить... . |
Здесь тоже есть опция capture, но недокументированная
http://docs.sencha.com/extjs/6.0/6.0...xt.util.KeyMap http://docs.sencha.com/extjs/6.0/6.0...xt.util.KeyNav плюс еще опция priority тоже есть. |
Цитата:
Цитата:
Цитата:
Цитата:
|
Цитата:
Но ведь опция { capture: true } гарантированно решает эту проблему. Зачем "максимально использовать возможности браузера", если проблема перехвата так легко решается? Хотя бы ценой смены события keypress на keydown. |
Цитата:
А самое главное: для обработки этих событий вам нужно писать код, а код === баги === стоимость поддержки. Единственный способ уменьшить количество багов, это уменьшить количество кода. Но вы не подумайте чего, я не буду вас уговаривать. Ваш проект, ваша головная боль... ;) |
Цитата:
|
Цитата:
|
Первая версия универсального контроллера ввода штрих-кодов. Для его использования по идее надо сделать контроллер обработки, который подписывается на событие barcode.
/* global Ext */
/**
* Контроллер ввода штрих-кодов.
*/
Ext.define("Ews.controller.Barcode", {
extend: "Ext.app.Controller",
requires: ["Ews.util.EventUtil"],
barcodePrefix: "$",
barcodeSuffix: "&",
//barcodeSuffixCode: 13,
mode: "listen", // listen | scan
value: "",
init: function() {
var me = this;
// Событие keypress похоже надо заменить на keydown
// [url]http://javascript.ru/forum/extjs/63113-ext-focusmanager-2.html[/url]
Ext.getWin().on("keypress", "onWindowKeyPress", me, { capture: true });
console.group("Слежение за штрих-кодами включено.");
console.log("Префикс:", me.barcodePrefix);
console.log("Суффикс:", me.barcodeSuffix);
console.groupEnd();
},
onWindowKeyPress: function(event) {
var me = this;
var keyChar = Ews.util.EventUtil.getCharFromEvent(event);
if (me.mode == "scan") {
if (keyChar == me.barcodeSuffix) {
//if (event.keyCode == me.barcodeSuffixCode) {
me.end();
} else {
me.resume(keyChar);
}
} else {
if (keyChar == me.barcodePrefix) me.begin();
}
},
begin: function() {
var me = this;
me.value = "";
me.mode = "scan";
console.log("Начат ввод штрих-кода.");
},
resume: function(keyChar) {
var me = this;
me.value += keyChar;
console.log("Ввод штрих-кода продолжается:", keyChar, me.value);
},
end: function() {
var me = this;
me.mode = "listen";
me.onEnterBarcode(me.value);
console.log("Ввод штрих-кода завершен. Введенный код:", me.value);
},
onEnterBarcode: function(barcodeValue) {
var me = this;
me.fireEvent("barcode", barcodeValue);
}
});
И вспомогательная утилитка:
/* global Ext */
Ext.define("Ews.util.EventUtil", {
singleton: true,
/**
* Получение кода символа из события keypress.
* Вспомогательная функция.
* event.type должен быть keypress (функция работает только с keypress).
* [url]https://learn.javascript.ru/keyboard-events[/url]
*/
// Привести в соответствие со свойством browserEvent
// [url]http://docs.sencha.com/extjs/6.0/6.0.1-classic/#!/api/Ext.event.Event-property-browserEvent[/url]
getCharFromEvent: function(event) {
//event = event instanceof Ext.event.Event ? event.browserEvent : event;
if (event.which == null) { // IE
if (event.keyCode < 32) return null; // спец. символ
return String.fromCharCode(event.keyCode);
}
if (event.which != 0 && event.charCode != 0) { // все кроме IE
if (event.which < 32) return null; // спец. символ
return String.fromCharCode(event.which); // остальные
}
return null; // спец. символ
}
});
|
| Часовой пояс GMT +3, время: 00:04. |