HTML contenteditable with non-editable islands

I'm a CKEditor developer. We've got some experience with nested readonly elements (i.e. placeholder plugin and the feature we're working on currently #9764) and I don't have good news. You have to handle every behaviour manually if you want to have consistent look&feel. There are no tricks that will fix browsers. And many things (like this with weird things happening around input on GC) seem to be unsolvable.


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).


Solution below -- with a small caveat. But first -- you guys are awesome!
I am not a JS or HTML expert, but I also did not know about jsfiddle.net. So reading your comments plus other online searches, and some testing on jsfiddle I created the following code that worked.

<pre><code>
<div contenteditable="false" class="editor" readonly="true" UNSELECTABLE="ON">
    Sample template with <span class="mergecode test1" />.
    <span contenteditable> editable </span>
    <font color=red><span UNSELECTABLE="ON" contenteditable="false" readonly="true"
     UNSELECTABLE="ON">  uneditable1 </span></font>
    <span contenteditable> editable2 </span>
    <font color=red><span contenteditable=false readonly="true" UNSELECTABLE="ON"> uneditable3</span></font>  
    <span contenteditable> editable4 </span>
    <span contenteditable="false" readonly="true" UNSELECTABLE="ON"> uneditable5 </span>
< /div>
</code></pre>

If you put this into jsfiddle on chrome you see that it works in chrome (and IE and FF but a bit differently), but there seems to be a small Caveat.

The caveat is what your key binding does with Backspace! In my case, when I select an uneditable phrase and press backspace in chrome, it seems to refresh, or do a "go to the last page"! I made the first two undeditbles red (left the last black) so that you can see what happens. I test it on jsfiddle in Chrome and in FF and in IE. All seems to have acted properly, give or take the backspace issue.

And I think here is the logic of how it works. The first Div is the top level parent, and it is contenteditable="false". So Everything under it should be un-editable. EXCEPT the children within it that have span contenteditble="true" they become editable. So you inherit from your parent, but then you can overwrite as a child.

I also put the tags readonly="true" UNSELECTABLE="ON" because I read about them some place. And it took more testing!!! NOTE PLEASE I had to take Font... outside of the span, or it would not work. But in IE it worked perfectly. The uneditables were whatever color I told them to be and no amount of single, dourble, or triple clicking would select the uneditbles in jsfiddle!!! :-) For Chrome single click did nothing, double click selected the uneditable, and with backspace it went a bit nuts! It seemed that would go to the previous page. With FF things were a bit crazier! Double click selected the uneditable and the editable before it! No idea why.

If someone can figure out and write about the issues with Chrome and FF that would be great. It would be nice to have double click or any number of fast clicks not to work in Chrome and FF too. Again I want to emphasize the span... seems to have to be next to the uneditables and font... outside of the span.

Hope this all makes sense.


I know its an old thread, which I came across with similar problem. Just needed few non editable spans in editable div. Ended up implementing a HACK-AROUND like...

$('#testDiv').on('keydown', function(e) { 
    var targetNode = getSelectionStart();
    if(targetNode != undefined && targetNode.nodeType === 1 && targetNode.nodeName == 'SPAN')    
    {
      var nodeHtmlString = targetNode.outerHTML;

      if(~nodeHtmlString.indexOf("nonEditable"))
      {
        e.preventDefault();
        e.stopPropagation();
      }
    }
});

function getSelectionStart() {
 var node = document.getSelection().anchorNode;
 return (node.nodeType == 3 ? node.parentNode : node);
}

Complete fiddle at https://jsfiddle.net/fxmb3nL6/20/