I'm developing a contentEditable region on my website, where users will be able to type messages to each other.
<div contentEditable="true" class="smartText">User types here...</div>
The thing is, we will have smart text inside, meaning that if a user type @usersame
inside this div, the @username
should be highlighted in blue if the username exist and green if he doesn't exist. And of course all of this should happen as the user types...
I have no idea where to start, right now I have this:
$("body").on("keyup",".smartText",function(){
var $this = $(this),
value = $this.html(),
regex = /[^>]#\S+[^ ]/gim;
value = value.replace(regex,"<span style='color:red'>$&</span>");
$this.html(value);
});
But the text keeps jumping (as well as the caret position) and doesn't feel like the right direction. I guess it's a little similar to JSFiddle which colors code as it finds it. I basically want the same thing as Twitter has.
Here is a JSFiddle to play around with: http://jsfiddle.net/denislexic/bhu9N/4/
Thanks in advance for your help.
The method you are using seems very browser intensive and may cause some issues if someone types very quickly and it's running multiple requests before the 'String' can be verified through ajax. You might be better off if you use a library such as http://aehlke.github.io/tag-it/ - You can depict a function to change font color, etc, the same way it recommends a tag.
If i get time, i will make fiddle demo.
As Ezos pointed out in his answer, I would not recommend trying to do anything intensive (such as making Ajax requests to check if a username exists or not) each time the user releases a key. You might have a bad time. With that said, I would recommend waiting a set amount of time after the user has stopped typing to run through what they've typed and highlight words, for example:
Link to a fiddle: http://jsfiddle.net/neJLW/
I think the code above should get you started in the right direction. Like you said, the cursor will still jump around so you'll have to save it and reset it in its old position each time you edit the contents of the
div
. Also, you'll want to adjust the timeout according to how long you expect determining if a username exists to take. You'll need to replace// Magic
with your username check and adjust the return value accordingly.As an aside, you'll want to keep in mind the accessibility issues with wrapping certain things in
span
s (see this GitHub issue for Lettering.js for an example).Edit: Also note that this is not a robust solution (it doesn't react to copy paste for example). YMMV.
This seems to be somewhat a solution to your problem.
DEMO here: http://jsfiddle.net/bhu9N/5/
Once the text is highlighted, the cursor goes to the starting of the div. So this still needs to be fixed. I will be updating the solution if I am able to find it. Meanwhile, just play around with what I have provided now and see if it helps.
I liked this problem and I worked very hard to solve. I believe I have finally succeeded (with a little assistance).
= UPDATED =
Piece of Code:
Screenshot:
JSFiddle here: http://jsfiddle.net/hayatbiralem/9Z3Rg/11/
Needed External Resources:
Helper Question (thanks): replace innerHTML in contenteditable div
Regex Test Tool (thanks): http://www.pagecolumn.com/tool/regtest.htm
Keep in mind that the HTML markup typed by the user could be quite surprising, e.g:
<span>@use</span><span>rname</span>
, which still looks like@username
to the user.To avoid the crazy caret behavior (and some other nasty side effects) inside a
contentEditable
element, you should use W3C DOM API and walk the DOM tree each time there is a change in HTML (you can sniff the change by pollingbody.innerHTML
upon a timer).I've recently answered a similar question for CKEditor and described the algorithm of how to build a text-to-node map of the DOM, for finding a text match. The CKEditor DOM API is quite similar to the W3C one, you can adapt the same algorithm.
Once the match has been found, you should use DOM Range API to manipulate the content of the DOM nodes. E.g., to wrap a run of plain text with a styled
<SPAN>
:Overall, this task is quite non-trivial and certainly isn't something you can fit into a single page of JavaScript code, to be answered here.