HTML Contenteditable with Non-Editable Islands

HTML contenteditable with non-editable islands

One more idea that looks promising:

To use empty span with ::before { content:"caption"; } that should produce non editable block represented in in DOM as a node having no caret positions inside.

You can try it here http://jsfiddle.net/TwVzt/1/

But this approach is not free of problems (in my case): There is no way to declare ::before inline using style DOM attribute and so the whole set should be declared in CSS upfront.
But set of merge codes I have is pretty large, even unknown upfront in full in some use cases. Sigh.

Nevertheless putting this recipe here if someone will have better circumstances (known set of codes).

Newlines and contenteditable with nested non-editable tags

Make each span contenteditable, then the user won't be able to delete the span you don't want edited, and get rid of the div being editable.

Here's my version of the JSFiddle: http://jsfiddle.net/howderek/HgRsF/2/

Selection and deletion problems with Non-Editable Span inside ContentEditable DIV

The challenge to this is to get IE11 to backspace from the right directly against the <span>.  Then the next backspace will select and highlight it.  This seems like such a simple objective, but IE11 just won't cooperate.  There should be a quick easy patch, right?  And so the bugs begin.

The approach I came up with is to walk the tree backwards to the first previous non-empty node, clearing the empty nodes between to appease IE, and then evaluate a few conditions.  If the caret should end up at the right side of the <span>, then do it manually (because IE won't) by creating a new range obj with the selection there at the end of the <span>.

online demo

I added an additional kludge for IE in the case that two spans are dragged against eachother.  For example, Field2Field3.  When you then backspace from the right onto Field3, then backspace once again to delete it, IE would jump the caret leftward over Field2.  Skip right over Field2.  grrr.  The workaround is to intercept that and insert a space between the pair of spans.  I wasn't confident you'd be happy with that.  But, you know, it's a workaround.  Anyway, that turned-up yet another bug, where IE changes the inserted space into two empty textnodes.  more grrr.  And so a workaround for the workaround.  See the non-isCollapsed code. 

CODE SNIPPET

var EditableDiv = document.getElementById('EditableDiv');
EditableDiv.onkeydown = function(event) { var ignoreKey; var key = event.keyCode || event.charCode; if (!window.getSelection) return; var selection = window.getSelection(); var focusNode = selection.focusNode, anchorNode = selection.anchorNode;
var anchorOffset = selection.anchorOffset;
if (!anchorNode) return
if (anchorNode.nodeName.toLowerCase() != '#text') { if (anchorOffset < anchorNode.childNodes.length) anchorNode = anchorNode.childNodes[anchorOffset] else { while (!anchorNode.nextSibling) anchorNode = anchorNode.parentNode // this might step out of EditableDiv to "justincase" comment node anchorNode = anchorNode.nextSibling } anchorOffset = 0 }
function backseek() {
while ((anchorOffset == 0) && (anchorNode != EditableDiv)) {
if (anchorNode.previousSibling) { if (anchorNode.previousSibling.nodeName.toLowerCase() == '#text') { if (anchorNode.previousSibling.nodeValue.length == 0) anchorNode.parentNode.removeChild(anchorNode.previousSibling) else { anchorNode = anchorNode.previousSibling anchorOffset = anchorNode.nodeValue.length } } else if ((anchorNode.previousSibling.offsetWidth == 0) && (anchorNode.previousSibling.offsetHeight == 0)) anchorNode.parentNode.removeChild(anchorNode.previousSibling)
else { anchorNode = anchorNode.previousSibling
while ((anchorNode.lastChild) && (anchorNode.nodeName.toUpperCase() != 'SPAN')) {
if ((anchorNode.lastChild.offsetWidth == 0) && (anchorNode.lastChild.offsetHeight == 0)) anchorNode.removeChild(anchorNode.lastChild)
else if (anchorNode.lastChild.nodeName.toLowerCase() != '#text') anchorNode = anchorNode.lastChild
else if (anchorNode.lastChild.nodeValue.length == 0) anchorNode.removeChild(anchorNode.lastChild)
else { anchorNode = anchorNode.lastChild anchorOffset = anchorNode.nodeValue.length //break //don't need to break, textnode has no children } } break } } else while (((anchorNode = anchorNode.parentNode) != EditableDiv) && !anchorNode.previousSibling) {} } }
if (key == 8) { //backspace if (!selection.isCollapsed) {
try { document.createElement("select").size = -1 } catch (e) { //kludge for IE when 2+ SPANs are back-to-back adjacent
if (anchorNode.nodeName.toUpperCase() == 'SPAN') { backseek() if (anchorNode.nodeName.toUpperCase() == 'SPAN') { var k = document.createTextNode(" ") // doesn't work here between two spans. IE makes TWO EMPTY textnodes instead ! anchorNode.parentNode.insertBefore(k, anchorNode) // this works anchorNode.parentNode.insertBefore(anchorNode, k) // simulate "insertAfter" } } }

} else { backseek()
if (anchorNode == EditableDiv) ignoreKey = true
else if (anchorNode.nodeName.toUpperCase() == 'SPAN') { SelectText(event, anchorNode) ignoreKey = true } else if ((anchorNode.nodeName.toLowerCase() == '#text') && (anchorOffset <= 1)) {
var prev, anchorNodeSave = anchorNode, anchorOffsetSave = anchorOffset anchorOffset = 0 backseek() if (anchorNode.nodeName.toUpperCase() == 'SPAN') prev = anchorNode anchorNode = anchorNodeSave anchorOffset = anchorOffsetSave
if (prev) { if (anchorOffset == 0) SelectEvent(prev)
else { var r = document.createRange() selection.removeAllRanges()
if (anchorNode.nodeValue.length > 1) { r.setStart(anchorNode, 0) selection.addRange(r) anchorNode.deleteData(0, 1) } else { for (var i = 0, p = prev.parentNode; true; i++) if (p.childNodes[i] == prev) break r.setStart(p, ++i) selection.addRange(r) anchorNode.parentNode.removeChild(anchorNode) } } ignoreKey = true } } } } if (ignoreKey) { var evt = event || window.event; if (evt.stopPropagation) evt.stopPropagation(); evt.preventDefault(); return false; } }
function SelectText(event, element) { var range, selection; EditableDiv.focus(); if (window.getSelection) { selection = window.getSelection(); range = document.createRange(); range.selectNode(element) selection.removeAllRanges(); selection.addRange(range); } else { range = document.body.createTextRange(); range.moveToElementText(element); range.select(); } var evt = (event) ? event : window.event; if (evt.stopPropagation) evt.stopPropagation(); if (evt.cancelBubble != null) evt.cancelBubble = true; return false; }
#EditableDiv {          height: 75px;          width: 500px;          font-family: Consolas;          font-size: 10pt;          font-weight: normal;          letter-spacing: 1px;          background-color: white;          overflow-y: scroll;          overflow-x: hidden;          border: 1px solid black;          padding: 5px;        }        #EditableDiv span {          color: brown;          font-family: Verdana;          font-size: 8.5pt;          min-width: 10px;          /*_width: 10px;*/          /* what is this? */        }        #EditableDiv p,        #EditableDiv br {          display: inline;        }
<div id="EditableDiv" contenteditable="true"> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field1</span> < 500)  <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>OR</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field2</span> > 100 <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>AND</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field3</span> <= 200) )</div>

contenteditable=false inside contenteditable=true block is still editable in IE8

Okay, I already have discovered the answer much like how Penicillin was discovered.

You see, playing around with this code, I mistakenly set contenteditable to true for the span and voila! It worked!

So, to make a span NON-contenteditable inside a contenteditable div, you just set its contenteditable attribute to true!

<div contenteditable="true">
Luke, I am your father.
<span contenteditable="true">I'm your son?! Ewww!</span>
Don't speak back to me!
</div>

Here's the file to demonstrate (use IE8 to open it): https://codepen.io/hgezim/pen/qMppLg .

Lastly, I didn't post the question to get votes (although, they wouldn't hurt!), but since the solution was so ridiculous and I didn't find it here, I thought someone may find this tip time saving.

Focusing contentEditable=true parent by clicking on contentEditable=false child nodes

Check this:
HTML contenteditable with non-editable islands

Basic idea is to use empty span with ::before { content:"caption"; }

span.non-editable::before {   content:attr(name);   background:gold;   display:inline-block;  }
<div class="editable" contentEditable="true">Some more text here  <span class=non-editable name="placeholder"></span>  Hi this is my content</div>


Related Topics



Leave a reply



Submit