События клавиатуры на [не] контент-ориентированных элементах 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'> &#9653;
    <span class='momnode_cl'>*<span class='momconn_cl'>
    <span class='momitemref_cl'>comment</span></span>
    (&#8220;<span class='momstring_cl'>some simple notice</span>&#8221;
     <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 является беспорядочным (поэтому я скорее избегаю его) и больше не работает в Firefox (например, даже недавняя бета-версия Firefox не знает о contenteditable='events', см. nsGenericHTMLElement.h файл)

  • событие пузыря и захват событий имеет большое значение

  • нормальный <div> (или <span>) можно сделать фокусируемым, предоставив ему свойство tabindex

  • выбор текста API может быть полезен (но имеет несколько последних ошибки)

Поэтому мне, вероятно, не нужно 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'>&#9653; <span class='momnode_cl'>*<span class='momconn_cl'>
    <span class='momitemref_cl'>comment</span></span>(&#8220;<span class='momstring_cl'>some simple notice</span>&#8221; <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'> &#9653;
    <span class='momnode_cl'>*<span class='momconn_cl'>
    <span class='momitemref_cl'>comment</span></span>
    (&#8220;<span class='momstring_cl'>some simple notice</span>&#8221;
     <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/>