События клавиатуры на [не] контент-ориентированных элементах HTML5
Я кодирую монитор MELT (бесплатное программное обеспечение, альфа-этап, связанный с GCC MELT для определения GCC). Он использует libonion, чтобы вести себя как специализированный веб-сервер, и я хочу, чтобы он стал синтаксическим ориентированным редактором некоторых DSL, которые я разрабатываю, Я говорю о фиксации 97d60053, если это имеет значение. Вы можете запустить его как ./monimelt -Dweb,run -W localhost.localdomain:8086
, затем откройте http://localhost.localdomain:8086/microedit.html в своем браузере.
Я испускаю (через файл webroot/microedit.html
)
<h1>Micro Editing Monimelt</h1>
<div id='microedit_id' contenteditable='true'>*</div>
<hr/>
то некоторые обманщики AJAX заполняют этот элемент #micredit_id
чем-то содержимым, похожим на:
<dd class='statval_cl' data-forattr='notice'> ▵
<span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>comment</span></span>
(“<span class='momstring_cl'>some simple notice</span>”
<span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>web_state</span></span>
(<span class='momnumber_cl'>2</span>)</span>
<span class='momitemval_cl'>hashset</span>
<span class='momset_cl'>{<span class='momitemref_cl'>microedit</span>
<span class='momitemref_cl'>the_agenda</span>}</span>
<span class='momtuple_cl'>[<span class='momitemref_cl'>web_session</span>
<span class='momitemref_cl empty_cl'>~</span>
<span class='momitemref_cl'>the_system</span>]</span>)</span> ;</dd>
Теперь я хочу, чтобы каждый <span>
класса momitemref_cl
был чувствителен к некоторым событиям клавиатуры (и, возможно, мыши). Тем не менее, элементы contenteditable
могут редактироваться многими действиями пользователя (я даже не понимаю, что такое весь список таких действий пользователя...) и Я хочу, чтобы эти элементы span реагировали на ограниченный набор нажатий клавиш (буквенно-цифровой и пространственный) и не может быть изменен пользователем в противном случае (например, никакие знаки пунктуации, нет "вырезать", "вставить", нет backspace, no tab и т.д.).
Есть ли полный список событий (или действий пользователя), которые элемент contenteditable='true'
может получить и реагирует на?
Как отключить большинство этих событий или действий пользователя (на клавиатуре и мыши) и реагировать только на некоторые (четко определенные) события клавиатуры?
По-видимому, элемент <span>
в элементе non contenteditable
не может получить никакого действия пользователя клавиатуры (потому что он не может получить фокус)...
Я занимаюсь только недавними браузерами HTML5, такими как Firefox 38 или 42, или Chrome 47 и т.д. на Debian/Linux/x86-64, если это имеет значение (поэтому мне действительно не важно IE9)
PS. this является связанным вопросом, но не тем же.
PS2: Найден почему contenteditable
является ужасной страницей блога. Заставляет меня почти плакать... Также читал о подделке редактируемого элемента управления в браузере Javascript (для CodeMirror). См. Также проект внутреннего документа W3C на Редактирование объяснения и редактировать события черновик. Обе вещи W3C работают в процессе. W3C TR на События пользовательского интерфейса по-прежнему (ноябрь 2015) рабочий проект. См. Также http://jsfiddle.net/8j6jea6p/ (который ведет себя по-разному в Chrome 46 и в Firefox 42 или 43 beta)
PS3: возможно, a contenteditable
- это, в конце концов, плохая идея. Я (к сожалению) рассматриваю использование canvas
(à la carota) и делает все редактирование и рисование рукописным javascript...
Приложения:
(26 ноября th 2015)
Обсуждая конфиденциально с некоторыми пользователями Mozilla, я понял, что:
Поэтому мне, вероятно, не нужно contenteditable
Ответы
Ответ 1
Вы можете сделать так:
function validateInput(usrAct){
swich(usrAct){
case "paste":
// do something when pasted
break;
case "keydown":
// dosomething on keydown
break;
default:
//do something on default
break;
}
}
document.querySelectorAll('.momitemref_cl').addEventListener('input', function(e){
validateInput(e.type)
}, false);
Ответ 2
Этот фрагмент может быть тем, что вы ищете, делая элементы span.momitemref_cl
настраиваемыми, но не tabbable, а параметр имеет contenteditable
. Но поскольку я тестирую его на chrome, contenteditable
внутри любого контейнера с атрибутом contenteditable
, установленным на true
, не запускайте никакое событие клавиатуры. Таким образом, фокус может быть сосредоточен, чтобы установить любой контейнер в не редактируемый (и снова включить размытие).
См. например: (события нажатия клавиш и keydown привязаны для обработки некоторых конкретных случаев, когда нажатие клавиши или keydown не запускается с помощью клавиш specialc)
ПРИМЕЧАНИЕ.. Кажется, вы динамически заполняете DIV контентом, вы можете делегировать его или связать событие (& установить атрибут tabindex, если изменить его в HTML-разметке, а не на решение) после завершения запроса ajax.
$('#microedit_id .momitemref_cl').attr('tabindex', -1).prop('contenteditable', true).on('focusin focusout', function(e) {
$(this).parents('[contenteditable]').prop('contenteditable', e.type === "focusout");
}).on('keypress keydown paste cut', function(e) {
if (/[a-zA-Z0-9 ]/.test(String.fromCharCode(e.which))) return;
return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h1>Micro Editing Monimelt</h1>
<div id='microedit_id' contenteditable='true'>
<dd class='statval_cl' data-forattr='notice'>▵ <span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>comment</span></span>(“<span class='momstring_cl'>some simple notice</span>” <span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>web_state</span></span>(<span class='momnumber_cl'>2</span>)</span> <span class='momitemval_cl'>hashset</span>
<span class='momset_cl'>{<span class='momitemref_cl'>microedit</span>
<span class='momitemref_cl'>the_agenda</span>}</span> <span class='momtuple_cl'>[<span class='momitemref_cl'>web_session</span>
<span class='momitemref_cl empty_cl'>~</span>
<span class='momitemref_cl'>the_system</span>]</span>)</span>;</dd>
</div>
<hr/>
Ответ 3
Во-первых, HTMLElements
станет contentEditableElements
, когда вы установите для атрибута contentEditable
значение true
.
Теперь лучший способ сделать ваш синтаксический анализ IMO - прослушать inputEvent
и проверить свой элемент textContent
:
s.addEventListener('input', validate, false);
function validate(evt) {
var badValues = ['bad', 'content'];
var span = this;
badValues.forEach(function(v) {
if (span.textContent.indexOf(v) > -1) {
// that bad m..key
span.textContent = span.textContent.split(v).join('');
}
});
};
<span id="s" contentEditable="true">Hello</span>
Ответ 4
Изменить:
(обрабатывает только промежутки с указанным классом. Также обрабатывает случай, когда вы можете вернуться из другого диапазона в предыдущий и удалить его. Включает идею @AWolff для переключения атрибута contenteditable на фокус)
Общая идея остается такой же, как и предыдущей.
Fiddle: http://jsfiddle.net/abhitalks/gb0mbwLu/
Отрывок:
var div = document.getElementById('microedit_id'),
spans = document.querySelectorAll('#microedit_id .momitemref_cl'),
commands = ['paste', 'cut'],
// whitelist is the keycodes for keypress event
whitelist = [{'range': true, 'start': '97', 'end': '122'}, // lower-case
{'range': true, 'start': '65', 'end': '90'}, // upper-case
{'range': true, 'start': '48', 'end': '57' } // numbers
],
// specialkeys is the keycodes for keydown event
specialKeys = [8, 9, 13, 46] // backspace, tab, enter, delete
;
div.addEventListener('keydown', handleFromOutside, false);
[].forEach.call(spans, function(span) {
span.setAttribute('contenteditable', true);
span.setAttribute('tabindex', '-1');
span.addEventListener('focus', handleFocus, false);
span.addEventListener('blur', handleBlur, false);
commands.forEach(function(cmd) {
span.addEventListener(cmd, function(e) {
e.preventDefault(); return false;
});
});
span.addEventListener('keypress', handlePress, false);
span.addEventListener('keydown', handleDown, false);
});
function handleFocus(e) { div.setAttribute('contenteditable', false); }
function handleBlur(e) { div.setAttribute('contenteditable', true); }
function handlePress(e) {
var allowed = false, key = e.keyCode;
whitelist.forEach(function(range) {
if (key && (key != '') && (range.start <= key) && (key <= range.end)) {
allowed = true;
}
});
if (! allowed) { e.preventDefault(); return false; }
}
function handleDown(e) {
var allowed = false, key = e.keyCode;
specialKeys.forEach(function(spl) {
if (key && (spl == key)) { e.preventDefault(); return false; }
});
}
function handleFromOutside(e) {
var key = e.keyCode, node = window.getSelection().anchorNode, prev, next;
node = (node.nodeType == 3 ? node.parentNode : node)
prev = node.previousSibling; next = node.nextSibling;
if (prev || next) {
if (node.className == 'momitemref_cl') {
if (specialKeys.indexOf(key) >= 0) {
e.preventDefault(); return false;
}
}
}
}
<h1>Micro Editing Monimelt</h1>
<div id='microedit_id' contenteditable='true'>
<dd class='statval_cl' data-forattr='notice'> ▵
<span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>comment</span></span>
(“<span class='momstring_cl'>some simple notice</span>”
<span class='momnode_cl'>*<span class='momconn_cl'>
<span class='momitemref_cl'>web_state</span></span>
(<span class='momnumber_cl'>2</span>)</span>
<span class='momitemval_cl'>hashset</span>
<span class='momset_cl'>{<span class='momitemref_cl'>microedit</span>
<span class='momitemref_cl'>the_agenda</span>}</span>
<span class='momtuple_cl'>[<span class='momitemref_cl'>web_session</span>
<span class='momitemref_cl empty_cl'>~</span>
<span class='momitemref_cl'>the_system</span>]</span>)</span> ;</dd>
</div>
<hr/>