samclarke / SCEditor

A lightweight HTML and BBCode WYSIWYG editor
http://www.sceditor.com/
Other
657 stars 187 forks source link

Uneditable HTML Elements??? #527

Open iam1me opened 8 years ago

iam1me commented 8 years ago

Hey all,

I'm trying to support some custom BBCode elements like 'data-field'. I've got a custom processor on the server side that will look for 'data-field' and replace it with data from a record, based upon the attribute values. That's all working, but now I'm trying to get the UI working - and SCEditor looks like a good choice.

I've already got the 'data-field' BBCode registered wtih the SCEditor so that it will correctly convert back and forth between BBCode and HTML. However, I want the HTML display to be purely informational (and maybe a context menu for editing via a dialog). I don't want them to be able to edit the content of the HTML div.

I attempted using the contenteditable="false" feature. This does prevent the user from editing the content of the div. However, it also makes it impossible to add any content to the left/right of the div from the HTML Editor. They also can't delete the div as a whole (vs just text within the div).

Is it possible to create a div or some other element that will allow me to display such fixed content inline with other text? I know other elements work similar (youtube videos, iframes, images, etc.).

Thanks! Ryan

iam1me commented 8 years ago

I've found that the Canvas element will probably give me the desired behavior; I can write text that can't be edited, it appears in line with text, and I can add/edit text around it. However, in order to actually write the text I need to be able to process the canvas with javascript once the element has been added to the page. Does the SCEditor API provide a means of listening for when elements are added to the WYSIWYG view?

brunoais commented 8 years ago

I get what you mean. The backspace works to delete the whole block, though.

Unfortunately, I don't have enough time to maintain this project. Unless its original author comes back... (again) Unfortunately this project won't have any future. Let's see if he appears sometime in the near future.

iam1me commented 8 years ago

Yea it would be a shame for this project to go under; it's the best free BBCode WYSIWYG editor I've seen, and I can tell alot of work has gone into it. If he confirms he won't be working on it further, maybe we'll see some spin offs.

iam1me commented 8 years ago

If anyone else is interested in this kind of feature, I was able to get it working by creating a custom plugin that uses the signalReady extension function to initialize a MutationObserver that listens for all added/removed nodes under the SCEditor Body. Whenever I see a canvas element with a data-field class, I am able to initialize the element (aka render the canvas based upon it's attributes). I also setup another MutationObserver specific to each such element, listening for attribute changes. Whenever a relevant attribute change occurs, I re-render the canvas. Finally, I setup a general event handler on the SCEditor Body for event context handling like so:

$("textarea").sceditor("instance").getBody().on('contextmenu', 'canvas.data-field', dataFieldContextMenuHandler);

I can then open a modal dialog for editing the properties of the data-field. Some more support from the API to make this kind of thing easier to do would be nice, but am quite pleased with the results nonetheless.

samclarke commented 7 years ago

I've been investigating the best way to implement this to allow uneditable HTML blocks to be added.

What I've found is contenteditable=false has a few issues:

Desktop browsers

Chrome works fine out of the box. IE won't remove contenteditable=false block elements but will remove inline blocks so can use them to work around it. Edge and FF won't remove any contenteditable=false elements.

It's not too hard to get FF to behave like Chrome by using selection.modify() to see if backspace or del should remove a block. Edge doesn't support selection.modify() so it requires normalising white space and then iterating over all the nodes between the block and the cursor to work out if there are any characters/newlines between the cursor and contenteditable=false element which is ugly but works.

Android browsers

None of the above works. The backspace event doesn't work in some keyboards (the default English keyboard being one of them) as it fires a keyboard event with the code 229 instead of the actual keycode. Also Chrome for Android closes the keyboard when it reaches a contenteditable=false element meaning even if the backspace event worked, you can't trigger it anyway.

You can do a nasty hack of inserting some zero width spaces after an inline and checking if they're remove which works for inlines. It has to be more than one zero width space as FF won't remove the character after a contenteditable=false element?!

To allow removing blocks it would have to be some kind click to remove behaviour which works for Chrome but not in FF as it won't remove the character after a contenteditable=false block. You could probably do an ugly hack of adding a zero width space to the first text node after a block to fix it though.

So I've given up on contenteditable=false as it's too complex to make work reliably. Using click to remove instead of backspace/del would work (if you fixed the weird Android FF bug) but isn't very user friendly. If any one wants to try, the code I came up with is here: https://jsfiddle.net/ttc7nx2c/

Iframes

iframes work well everywhere (all browsers tested can remove them fine) and they require much less code: https://jsfiddle.net/cv92u4m3/ The only issue I've found so far is with FF when deleting via del it messes up the cursor but that looks easy to fix.

They're not as lightweight as contenteditable=false but they do actually work and have far fewer edge cases so I'm going to try to use them to implement this.