我正在对MELT monitor进行编码(自由软件,alpha阶段,与GCC MELT域特定语言相关,以自定义GCC)。它使用libonion作为专门的Web服务器,我希望它成为我正在设计的某些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元素能够响应定义和限制的一组按键(字母数字和空格)并且无法用户更改(例如没有标点字符,没有“剪切”,没有“粘贴” “,没有退格,没有标签等......)。
是否存在contenteditable='true'
元素可以获取并正在做出反应的事件(或用户操作)的完整列表?
如何停用大多数这些事件或用户操作(在键盘和鼠标上)并仅 < / strong>(定义明确)键盘事件?
显然,非<span>
元素中的contenteditable
元素无法获得任何键盘用户操作(因为它无法获得焦点)...
我目标仅最近的HTML5浏览器,例如Firefox 38或42,或Chrome 47等...如果重要(因此我真的不这么做)在Debian / Linux / x86-64上关心IE9)
PS。 this是一个相关问题,但不是同一个问题。
PS2:找到why contenteditable
is terrible博客页面。让我几乎哭了......还读了faking an editable control in browser Javascript(CodeMirror)。另请参阅关于Editing Explainer和edit events草案的W3C内部文件草案。 W3C的两件事都在进行中。 UI events上的W3C TR仍然是(2015年11月)工作草案。另请参阅http://jsfiddle.net/8j6jea6p/(在Chrome 46和Firefox 42或43 beta中的行为方式不同)
PS3:也许contenteditable
毕竟是一个坏主意。我(遗憾地)考虑使用canvas
(àlacarota)并进行所有编辑&amp;用手写的javascript绘图......
(2015年11月26日 th )
通过私下与一些Mozilla人讨论,我理解:
contenteditable
很混乱(所以我宁愿避免它),并且在Firefox中不再有用(例如,即使是最近的测试版Firefox也不知道contenteditable='events'
,请参阅{ {3}})
普通<div>
(或<span>
)可以通过赋予tabindex
属性
event bubbling and capturing API可能很有用(但最近有一些text selection)
所以我可能不需要contenteditable
答案 0 :(得分:4)
你可以这样做:
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);
答案 1 :(得分:3)
这个代码段可能就是您要找的内容,使span.momitemref_cl
个元素可以关注,但不能列表,并且设置为contenteditable
。但是,当我在chrome上测试它时,contenteditable
在属性contenteditable
设置为true
的任何容器内,不要触发任何键盘事件。所以诀窍可能在焦点上将任何容器设置为不可编辑(并切换回模糊)。
参见例如:(keypress和keydown事件都被绑定以处理某些特定情况,其中keypress或keydown不会在特定键上触发)
注意: 您似乎在动态填充DIV内容,您可以委托它或绑定事件(如果在HTML标记中更改它而不是解决方案,则设置tabindex属性)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;
});
&#13;
<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/>
&#13;
答案 2 :(得分:2)
首先,当您将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's bad m..key
span.textContent = span.textContent.split(v).join('');
}
});
};
<span id="s" contentEditable="true">Hello</span>
不幸的是,输入事件不是widely supported
因此,您可能需要添加onkeydown
和onpaste
以及onclick
个事件处理程序来捕获不支持的浏览器(a.k.a IE)。
答案 3 :(得分:2)
(只处理具有所述类的跨度。还处理案例,您可以从另一个跨度返回到前一个并且可以删除它。结合@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.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/>
除了通常处理跨度上的事件以及阻止/允许来自白名单和黑名单的键和/或命令之外;这段代码的作用是检查光标或编辑当前是否正在其他不受约束的跨度上完成。当使用箭头键从目标跨度中选择或移动时,我们禁止使用特殊键来防止删除等。
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;
}
}
}
}
我没有多少时间,因此仍然存在一个问题。并且,这是在从外部移动到目标跨度时禁止命令以及剪切和粘贴。
较旧版本仅供参考:
如果要允许的所有击键,您可以维护一个白名单(或黑名单,如果允许的命令数量更高)。同样,还要维护要阻止的所有事件的字典。
然后连接div
上的命令并使用event.preventDefault()
拒绝该操作。接下来,连接keydown
事件并使用白名单允许所有击键都在上面定义的允许范围内:
在下面的示例中,根据第一个范围,只允许使用数字和字母,并且根据第二个范围允许使用箭头键(以及pageup / down和space )。休息所有行动都被阻止/拒绝。
然后,您可以将其进一步扩展到您的用例。在下面的演示中试一试。
小提琴:http://jsfiddle.net/abhitalks/re7ucgra/
<强>段:强>
var div = document.getElementById('microedit_id'),
spans = document.querySelectorAll('#microedit_id span'),
commands = ['paste'],
whitelist = [ {'start': 48, 'end': 90}, {'start': 32, 'end': 40 }, ]
;
commands.forEach(function(cmd) {
div.addEventListener(cmd, function(e) {
e.preventDefault(); return false;
});
});
div.addEventListener('keydown', handleKeys, false);
function handleKeys(e) {
var allowed = false;
whitelist.forEach(function(range) {
if ((range.start <= e.keyCode) && (e.keyCode <= range.end)) {
allowed = true;
}
});
if (! allowed) { 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/>
已编辑,以解决不捕获特殊键的问题,尤其是在按下shift并且为keyCode
生成相同的keypress
时。添加了keydown
来处理特殊密钥。
注意: 这假设要发生在整个div
上。正如我在问题中看到的那样,只有span
s和嵌套的那些。没有其他元素。如果涉及其他元素并且您想要免除这些元素,那么您将需要仅将事件绑定到这些元素。这是因为,当父亲为contenteditable
并且没有对孩子开枪时,父母会捕捉孩子的事件。
答案 4 :(得分:1)
一种直接解决问题的方法是侦听最里面的元素触发的keydown
事件,并采取相应的措施。可以在下面找到示例代码片段:
HTML:
<div class="momitemref_cl" contenteditable="true">Foo Bar</div>
<input class="not-momitemref_cl"/>
<input class="momitemref_cl"/>
JS:
document.querySelectorAll('.momitemref_cl').forEach((el) => {
el.addEventListener('keydown', validateInput);
el.addEventListener('cut', e => e.preventDefault());
el.addEventListener('copy', e => e.preventDefault());
el.addEventListener('paste', e => e.preventDefault());
});
function validateInput(userAction) {
console.log(userAction);
if (userAction.ctrlKey) {
userAction.preventDefault();
return false;
}
let code = (userAction.keyCode ? userAction.keyCode : userAction.which);
if ((48 <= code && code <= 57 && !userAction.shiftKey) || (65 <= code && code <= 90) || (97 <= code && code <= 122) || code === 32) {
console.log(`Allowing keypress with code: ${code}`);
return true;
}
console.log(`Preventing keypress with code: ${code}`);
userAction.preventDefault();
return false;
}
这对<input>
元素以及将contenteditable
属性设置为true
的元素都有效。
JS小提琴:https://jsfiddle.net/rsjw3c87/22/
编辑:还添加了其他检查,以防止右键单击并复制/剪切/粘贴。直接通过contextmenu
事件禁用右键单击不起作用,因为某些浏览器和操作系统不允许您禁用该特定事件。