键盘事件在 contenteditable HTML5元素上

我正在编写MELT监视器 (免费软件,alpha阶段,与GCC MELT域特定语言相关以定制GCC)。 它使用libonion作为一个专门的Web服务器,我希望它成为我正在devise的一些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就是用包含类似于以下内容的东西来填充#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> 

现在,我希望类momitemref_cl每个<span>某些键盘(也许是鼠标)事件敏感。 然而,可以通过许多用户操作(我甚至不理解这些用户操作的整个列表是什么…)来编辑可contenteditable元素,而我只希望这些span元素能够响应定义和限制的按键 (字母数字和空格,否则不能被用户修改 (例如没有标点符号,没有“剪切”,没有“粘贴”,没有退格键 ,没有标签等)。

是否有contenteditable='true'元素可以获取和正在做出反应的事件(或用户操作)的完整列表?

如何禁用大部分这些事件或用户操作 (在键盘和鼠标上) 一些 (定义明确的) 键盘事件 作出反应

显然,一个非contenteditable元素中的<span>元素不能得到任何键盘用户操作(因为它不能获得焦点)…

针对最近的HTML5浏览器,如Firefox 38或42,或Chrome 47等…在Debian / Linux / x86-64(如此重要)(所以我真的不关心IE9)

PS。 这是一个相关的问题,但不是一回事。

PS2:发现为什么contenteditable是可怕的博客页面。 让我差点哭了…还读了关于在浏览器的Javascript ( CodeMirror )中伪造一个可编辑的控件 。 另请参阅W3C关于编辑讲解员和编辑活动草案的内部文件草案。 W3C的两件事情都在进行中。 关于UI事件的 W3C TR仍然是(2015年11月)的工作草案。 另请参阅http://jsfiddle.net/8j6jea6p/ (在Chrome 46和Firefox 42或43 beta中的行为不同)

PS3:也许一个contenteditable是一个坏主意。 我(可悲的是)考虑使用canvas (àla carota ),并通过手写的JavaScript做所有的编辑和绘图…


附加物:

(2015年11月26

通过与一些Mozilla人私下讨论,我了解到:

  • contenteditable是凌乱的(所以我宁愿避免它),并不再在Firefox中工作太多(例如,即使是最近的testing版Firefox也不知道contenteditable='events' ,请参阅nsGenericHTMLElement.h文件 )

  • 事件冒泡和捕获事情很重要

  • 一个普通的<div> (或<span> )可以通过给它一个tabindex属性来进行聚焦

  • 文本select API可能是有用的(但有一些最近的错误 )

所以我可能不需要contenteditable

Solutions Collecting From Web of "键盘事件在 contenteditable HTML5元素上"

你可以这样做:

 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').addEventlistner('input', function(e){ validateInput(e.type) }, false); 

这个片段可能是你正在寻找的,使span.momitemref_cl元素可以聚焦,但不可tabbable和设置contenteditable 。 但是,当我在chrome上测试它时, contenteditable属性contenteditable设置为true任何容器内,不要触发任何键盘事件。 所以技巧可能是关注于将任何容器设置为不可编辑(并切换回模糊)。

看例如: (keypress和keydown事件都绑定处理一些特定的情况下,按键或keydown将不会触发特定的键)

注: 你似乎动态地填充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/> 

首先, HTMLElements在将contentEditable属性设置为true时变为contentEditableElements

现在,解析IMO的最好方法是监听inputEvent并检查元素的textContent

 s.addEventlistner('input', validate, false); function validate(evt) { var badValues = ['bad', 'content']; var span = this; badValues.forEach(function(v) { if (span.textContent.indexOf(v) > -1) { // that's bad m..key span.textContent = span.textContent.split(v).join(''); } }); }; 
 <span id="s" contentEditable="true">Hello</span> 

编辑:

只处理所述类的跨度,也处理这种情况,你可以从另一个跨度返回到前一个跨度,并且可以删除它,并包含了@AWolff用于将contenteditable属性转换为焦点的想法

总体思路与前一版本保持一致。

小提琴: 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.addEventlistner('keydown', handleFromOutside, false); [].forEach.call(spans, function(span) { span.setAttribute('contenteditable', true); span.setAttribute('tabindex', '-1'); span.addEventlistner('focus', handleFocus, false); span.addEventlistner('blur', handleBlur, false); commands.forEach(function(cmd) { span.addEventlistner(cmd, function(e) { e.preventDefault(); return false; }); }); span.addEventlistner('keypress', handlePress, false); span.addEventlistner('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/>