39

I have sort of browser based WYSIWYG editor where users can edit documents-templates.

Document-template is an ordinary html with some special "merge code placeholders". Such template gets "instantiated" by replacing these placeholders by data coming from DB. This gives final document - an instance of the template.

My current approach looks like this:

<div contenteditable>
  Sample template with <input type=button class="mergecode" value="MergeCode1">.
</div>

(Online sample to play with: http://jsfiddle.net/tFBKN/ )

The input is not editable in such case and behaves as a solid block - exactly what I need. Users can delete such merge codes by clicking DEL or BACKSPACE as any other characters, etc. By having proper CSS for such input.mergecode I am able to achieve look-n-feel I want.

But with such approach I have three different problems in three different UAs:

  • IE - CSS { font:inherit } simply does not work there, so if the input is inside <b> like here <b><input value="test"></b> it does not inherit any font styles.
  • FF - Copy of fragment that contains <input> element removes that input from clipboard content so further paste operation inserts everything but not inputs.
  • GC - Click on {BACKSPACE} immediately after the <input> produces weird results (bug)

So I am looking for other ideas of how to represent non-editable inline-block alike "islands" in HTML.

Other approach that I've tried so far:

  • <span contenteditable="false">MergeCode1</span> - it does not work as most of UAs remove such node from selection. So it is not possible to, say, apply <b> or <i> on top of selection that contains such span.

Any other ideas?

c-smile
  • 26,734
  • 7
  • 59
  • 86
  • 1
    Not possible. `contenteditable` affects all child elements. You could use JS to replace if deleted. – Mooseman Jan 30 '13 at 23:20
  • @Mooseman Sorry, what exactly is not possible? – c-smile Jan 31 '13 at 02:27
  • 1
    @Mooseman’s comment is wrong; `contenteditable` can be overridden for child elements, as in the example `MergeCode1`. As the question explains, this natural approach however does not work well with selections (e.g., triple clicking does not select an entire block). – Jukka K. Korpela Jan 31 '13 at 05:09
  • 2
    Try this code: `
    Hello, world! Unedtiable Hello, world!
    ` As you can see, the span can be removed still. The text 'Uneditable' cannot be edited, but the `` can be removed as a **whole** by the user. The Chrome dev tools show that the entire block has been removed from the DOM.
    – Mooseman Jan 31 '13 at 14:58
  • @Mooseman, as I said I've tried contenteditable="false" already. It does not work in case when you need to apply span like `` on top of such island. Try this: http://jsfiddle.net/RYsvZ/1/, select text with the MergeCode1 inside and click CTRL-I for example. You will see text italicized but not the MergeCode1. Use DOM inspector to see the problem. – c-smile Jan 31 '13 at 16:58
  • Interesting how the ``s are applied. I haven't found any spec related to the behavior of child elements, except that the default is `inherit`. The behavior that you described, which I see in Chrome, where the `` is not italicized or bolded, seems correct to me. `contenteditable` applies to the changing of text, but also formatting. JS could help here, but I assume that you don't want to go there now. – Mooseman Jan 31 '13 at 17:09
  • @Mooseman: I don't want to change the content of contenteditable=false element I just want to wrap selection that contains it into `` span. Chrome prevents this for some unknown reasons. – c-smile Jan 31 '13 at 19:43
  • The way I see it, but not bolding the span, Chrome is respecting the contenteditable. – Mooseman Jan 31 '13 at 19:46
  • @c-smile Did you end up sticking with the method? Or did you go the route of handling all events manually? The tradeoff here seems unfortunate :( – sthomps Oct 20 '13 at 16:59

6 Answers6

28

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.

Reinmar
  • 21,729
  • 4
  • 67
  • 78
  • Thanks for the response, seems like it is not just my problem. This fact makes this peel slightly less bitter but still. Nevertheless thanks for the answer. – c-smile Feb 04 '13 at 22:37
  • @Reinmar Late to the party, but... have you figured out any workaround for this issue ? cheers! – pelican_george Jun 20 '16 at 13:57
18

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

c-smile
  • 26,734
  • 7
  • 59
  • 86
  • 2
    Worked like a charm! Used `data-attributes` for having a dynamic content inside `::before`. – Artyom Neustroev Apr 30 '14 at 10:08
  • Yes, this is a nice solution. I also found this technique here : "HTML5 data attributes" : https://hacks.mozilla.org/2012/10/using-data-attributes-in-javascript-and-css/ and a video on that page : https://www.youtube.com/watch?v=On_WyUB1gOk (6 minutes) – Roelof Berkepeis Aug 02 '14 at 18:49
  • THANK YOU SO SO MUCH! This approach combined with dynamic text via data-attribute cleared a major roadblock for me. – Floscher Aug 01 '16 at 14:08
  • 1
    To add further light to this post and comments: `` and `span:before{content:attr(data-something);}` And it's easy enough to create a `` block with the pseudo content and styles all generated dynamically with JS and then append it to the head. – Modular Nov 01 '17 at 01:05
  • With `Ctrl-a` one can delete the element. – Friedrich -- Слава Україні Oct 05 '21 at 21:46
  • One more addition, after adding an element with `data-attribute`, add one more text node with "\u200B" which is zero width space, which allows cursor to move around non editable text. And gives better user experience. – Akash Kava Dec 06 '22 at 12:44
4

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/

Pravin
  • 650
  • 1
  • 7
  • 17
2

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.

user96265
  • 510
  • 5
  • 6
0

Here's a solution I'm using in a project :

<div>
  <span contenteditable>Editable text</span> <span> - Non editable text</span>
</div>

With the corresponding CSS to see the result :

div {
  border: 1px solid black;
  display: inline-block;
  max-width: 200px;
}

Here's the result : http://www.cssdesk.com/rULJM

tomfl
  • 707
  • 1
  • 11
  • 29
-3

For IE, try this out:

<span contenteditable="true">Uneditable</span>

Note the reverse value of the contenteditable attribute! It defies all common sense, but it works - at least in IE8 and IE9. This uneditable element you can move around and copy/paste. It’s however far from perfect. E.g. if you bold a region that encompasses the uneditable element, this will infect the content of the uneditable element as well. So you’ll probably have to use some scripting to keep the sanity of the element. Also setting contenteditable="true" will decorate the uneditable element with resize handles. You can turn them off again by setting unselectable="on", but this will prevent the object from being dragged around. Again you’ll probably have to rely on a script restoring the width and height.

meringue
  • 7
  • 1
  • Unfortunately this approach does not work in GC and FF - if you select the text that contains such `` and call `document.execCommand('bold')` to apply `` to the selection the span will be excluded from the selection. You will see something like this: `text beforeTesttext after`. – c-smile Jan 31 '13 at 19:33