Prevent Events in ContentEditable
by abhitalks
HTML
<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/>
CSS
span.momitemref_cl { background-color: yellow; }
JavaScript
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...