contenteditable div backspace and deleting text node problems - javascript

There are so many issues with contenteditable divs and deleting html and/or non content editable content inside editable divs.
Using an answer by the excellent Tim Down here: How to delete an HTML element inside a div with attribute contentEditable?
Using Tim's code, the entire text node gets deleted. I need this to work like any textarea would, deleting character by character and just making sure html elements can be backspaced as well.
I tried the following
else if(node){
var index = node.length-1;
if(index >= 0)
node.deleteData(index,1);
else
this.removeChild(node);
}
But this is obviously not going to work correctly. If I am at the end of the content, things work as expected. But if I place the cursor anywhere else, it's still deleting from the end.
I'm lost at this point, any help is very appreciated
http://jsfiddle.net/mstefanko/DvhGd/1/

After breaking down how google uses contenteditable divs in their google plus user tagging, I landed on a much more reasonable solution. Maybe it will help someone else out.
After adding 1 tag, you can already see a lot of differences in the html browser to browser.
In Google Chrome, a space is added with each tag. The button tag is used. And the chrome-only contenteditable="plaintext-only" is used.
When I backspace the space in chrome, a BR tag is then appended.
In Firefox the BR tag is added immediately with the first tag. No spaces are needed. And an input tag is used instead of the button tag.
The BR tag was the single greatest break-through I had while digging through this. Before adding this, there was a lot of quirky behavior with deleting tags, as well as focus issues.
In IE, more interesting changes were made. A span with contenteditable false is used for the tags here. No spaces or BR tags, but an empty text node.
With all of that, you don't have to copy google exactly.
The important parts:
If you're rendering HTML, do the following...
1. Chrome should use the button tag
2. Firefox/IE should use the input tag
For range/selection you generally want to treat things like tags as a single character. You can build this into your range/selection logic, but the behavior of the input/button tags is much more consistent, and way less code.
IE behaves better in IE7-8 using a span. Just from a UI standpoint. But if you don't care if your site is pretty in old versions of IE, the input has the correct behaviour in IE as well as firefox.
3. Chrome only, use the contenteditable="plaintext-only" attribute on your editable div.
Otherwise, a lot of weird issues happen not only when a user tries to paste rich-text, but also when deleting html elements sometimes the styles can get transferred to the div, I noted many strange issues with this.
4. If you need to set the caret position to the end of the div, set the end of the range before the BR.
for FireFox:
range.setEndBefore($(el).find('br')[0]);

Related

Text renders blank in Chrome, reappears when selected

The problem
I'm using innerHTML to put HTML-formatted text in a <div>. At a consistent, seemingly random point in the text, the fonts stop being rendered and display blank. When I select the invisible text, it reappears.
Description of the code
We have a single <div id="text" inner-h-t-m-l="[[markup]]">.
The initial markup doesn't contain any data apart from empty segments with IDs:
<div id="segment-1"></div><div id="segment-2"></div>... etc
In Javascript, we loop over the IDs using querySelector (this is slow) and insert HTML into the each segment's innerHTML.
The framework used is Polymer 2.
Additional info; video
In the Chrome Dev Tools, the invisible text is shown as present in the DOM and seems to be no different from the text that renders correctly.
The font in the video is non-standard, but the problem also occurs when using system fonts.
Here's a video to illustrate the problem.
Here's a screenshot of a Chrome profiler run:
Edit:
After a discussion in the comments, I thought I should link the actual code.
Here's the element in question.
The relevant parts are:
<div id="segmented_text_content" inner-h-t-m-l="[[markup]]"></div>
_addPrimaryText(textStrings) {...}
_addSecondaryText(textStrings) {...}
Edit 2:
I found two potential workarounds for this, but neither one works well enough.
If I run this.querySelector('#text').innerHTML = this.querySelector('#text').innerHTML with a timeout of 3 seconds, it paints the text correctly.
When adding the text, if I use the async processArray function from this comment, it renders the text correctly, albeit very slowly because it updates the layout after every insertion.
With these two points, my working theory now is that Chrome updates the layout before the innerHTML attribute is fully assigned.
I also forgot to mention this project uses Shady DOM.

editable div: text-cursor gets set inside a tag

I have an editable div. The content of that div looks e.g. like that:
This is a <ins>new</ins> chapter.
(The tags are not visible, they are for styling)
If you set the text cursor in front of the "new" everything is fine. But if you set the text cursor behind the "new", the cursor is inside the < ins >-tag and new typed text is also inside the tag:
This is a <ins>new and very interesting</ins> chapter.
But it should look like that:
This is a <ins>new</ins> and very interesting chapter.
How can I set the text cursor behind the tag and prevent that new text is written inside the tag?
OK. The first idea was to made the
<ins contenteditable="false">new</ins>
Inside the contenteditable="true" element. Further reading (contenteditable=false inside contenteditable=true block is still editable in IE8) tells that this is not as always interpreted good in IE. In this post there is a hack answer (https://stackoverflow.com/a/7522910/1125465) but I really do not agree. It is just a mistake which will probably be repaired in the next versions of browsers.
Next I followed this link (HTML contenteditable with non-editable islands) and I haven;t got good news. There is no way of blocking the ins tag from editing so simple. First of all a little note:
If this isn't an additional functionality You must be sure it works as it should. As You wrote The user isn't allowed to write inside the -tag, so all the options:
working in almost every browser...
working with a little bug...
working but if someone...
must be rejected. So if someone turns the javascript off, it should work too. In that case I've come to the first conclusion (as always): server side verification MUST BE DONE.
This will prevent the user from destroying Your database and doing things he can't.
After server side verifying (and showing notification if something is wrong of course) it is going to be additional functionality. So we should do all we can now, to make it work (but now there is no obligation).
NICE LECTURE :)
https://stackoverflow.com/a/7232871/1125465
http://jsfiddle.net/X6Ab8/
**SOLUTION **
I propose something like... I know this sounds a little bit more like old days with milion tags, but really this will work and will be great.
Make an additional span element between the ins elements (for example using php:
$text = '<span contenteditable="true">'.$text.'</span>';
str_replace('<ins>', '</span><ins>', $text);
str_replace('</ins>', '</ins><span contenteditable="true">', $text);
Make this span editable, and only this span editable (not the block container). That's all. Solution is simple, clean, much more efficient and almost 100% safe. And nice...
ADDITIONAL SAFETY when using javascript hacks
If You need it to be done fully with javascript (maybe someone has idea how?), for total safety I would propose additionaly something like this:
Add data-noneditable-id="id" to each non editable element inside the main block editable container. Now every non editable element has it own unique id (can be done using jQuery for example using selector $("div#editable ins")).
Run a javascript that will run through all the objects that has attribute "data-noneditable-id" and save their innerHTML in array (for example: 1 => 'new', 2=> 'added', 3=> 'inserted', ...).
Now if someone edit any of them, You can easily repair them.
PS. This should also help a little... (https://stackoverflow.com/a/4979828/1125465).
Hope it helps! Best regards.

Unable to delete html consistently in contenteditable div

Mainly in Firefox, although it occasionally breaks in other browsers as well. If you check the jsfiddle below, especially if you go to the last tag and try to delete them all, you can't. Sometimes you can. It's not consistent.
<div id="testDiv" contentEditable="true">
Hey <input id="user-tag-1" class="ut ut-full-name" carpos="9" type="button" tabindex="-1" value="Rob"/>
</div>
I've seen this discussed on Mozilla's board as well as here, the workaround is generally to wrap the non contenteditable html in contenteditable <span>, <p>, <span>. Nothing I have seen actually fixes the problem for me. at least not completely. Even when I can delete the html, there's a lot of strange behavior.
Anyone know why this happens for contenteditable divs, and if it's just something that happens with non contenteditable html, is there a solution that would fix these issues, or do I have to write logic for the backspace and delete keys to check and delete the tags?
http://jsfiddle.net/mstefanko/qDkYq/
I answered this in detail here: https://stackoverflow.com/a/18069930/352335
As a summary, the way each browser handles contenteditable divs varies a lot. Digging through google plus's implementation of their posting widget is what gave me the inspiration I needed to fix this.
If you're going to render non-editable html elements inside a contenteditable:
For Chrome, use a <button> tag. And use the chrome only attribute contenteditable="plaintext-only" on the editable div. If you're appending html make sure to add a space with each element.
For firefox, use an <input> tag, and append a <br> element to the editable div. The <br> at the end of the div allows you to focus after elements, and fixed the issues I was having with deleting.

Firefox contenteditable image selected after drop - can't remove selection

If you drag-and-drop around an image in Firefox in a contenteditable area, sometimes the images will end up being selected like this:
Fiddle here: http://jsfiddle.net/zupa/qg5Qh/
You may need to drag-drop it a few times, I have this bug in like 20+% of the time.
I am using Firefox 13.0.1 on Windows 7
How to remove that selection? Any help is appreciated.
Ps:
It is not available as a range via document.getselection().getRangeAt(..)
Firefox does NOT add any HTML attributes, still if I hit save (custom CMS), and reload the page in contenteditable mode, the selection comes back. Seems to be an annoying bug.
It does it reliably when the image is within a word that is marked by Firefox as a spelling error. For example, here's your jsFiddle with the image moved into the middle of the word "Lorem": http://jsfiddle.net/timdown/qg5Qh/1/
It seems to be something to do with the styling applied to misspelled words. Add the word "Lorem" to the browser's dictionary and the image styling goes away.
You could switch spellchecking off using the spellcheck attribute. From what I can gather, you have to do this at the <body> level in Firefox because it doesn't seem to work on single contenteditable elements as it does for textareas.
Demo: http://jsfiddle.net/timdown/qg5Qh/2/

Weird issue in IE when setting element's innerHTML attribute to contain a script element

I'm having a weird issue in IE when I set an elements innerHTML attribute to a string that contains a script element.
What happens is, when innerHTML is set like:
domEl.innerHTML = "<script type=\"text/javascript\">alert(\"hello world\");</script>"
alert(domEl.innerHTML);
The alert box doesn't show any text, as if the script element was removed completely. In addition, checking the element's childNodes collection also shows that the script element is not present as domEl.childNodes.length = 0.
However, if you add some text before the script tag like so:
domEl.innerHTML = "start text<script type=\"text/javascript\">alert(\"hello world\");</script>"
alert(domEl.innerHTML);
The script element is present when the alert box is shown.
Why is this happening and how can I fix it properly? Is this a bug in IE? It works fine in the latest versions of Chrome and Firefox. I'm using IE 8 for this.
looks like a bug, or some weird security consideration in IE.
try using XMP tags around your text. it might work, but that depends on what you were trying to achieve.

Categories