

So I adjusted it to be just the span with a following nbsp tag like this. I also discovered that you must add contenteditable=false to each element within the parent element so that when the user clicks, the cursor doesn’t appear inside some randomish point instead of at the end of the content. But then I was like, most people will just backspace and if they are on their phone no way they can accurately touch it. My first idea was to have a hashtag with an x to remove it. I moved to just a simple span element with added to the end. I was using divs, with spans inside them. This repeats until there are no hashtags. So now when the user backspaces, the first backspace does nothing, but the next removes the hashtag. I decided to use nbsp because it is seen as a #text node not a text element like p for example. Some people add an empty paragraph from what I read. Explained in depth in this great article. I fixed this by adding which is html for non breaking space. One stupid, nonsensical issue I ran into right away was when the user hit backspace instead of one letter or hashtag being removed, ALL CONTENT WAS REMOVED. If you are reading this you probably have done all this too and may be having issues as well. I tried reading the MDN docs for range and well I didn’t quite grasp how the system works. My cursor would appear in random places or not appear, or not be where I wanted it to be. I got most of it working by using Google, reading some articles, reading stack questions… but it wasn’t working properly. At first I was all confident like This should be easy right? Then when the user selects one, add it to the list by removing the typing first, then add the new hashtag and move the cursor to the end. What I needed was to allow a user to enter hashtags and then send an ajax request to the backend to fetch a list of matching hashtags to display in a drop list below the input(auto suggestions). My use case makes it even harder and maybe yours does too.
#CONTENTEDITABLE GET PLAIN TEXT UPDATE#
For one, you need to have event listeners to listen for input then perform the proper actions to update the content and move the cursor to the end. The idea seems super simple, but in reality the problem becomes complex for several reasons. Or they worked partially, or only in one browser. So this is something I needed to do and all the examples I found didn’t work properly. Speed test of innerText vs Selection.This appears to work in all browsers.The downside to option 2 is that it's relatively slow, so you may run into issues if you trigger it onchange or something like that.

Out of the two I personally think the second option would be better in most cases with the first option you'll either get a quick-and-dirty regex solution or you devolve into implementing a full-blown HTML parser with CSS layout logic. So what you can do is record the user's current selection, change the selection to be the contents of your contenteditable div, call Selection.toString, then restore the user's initial selection. Firefox supports Selection.toString which has a similar behavior to innerText, but it only works on the currently-selected text.
