How to mimic JS ranges/selections with divs as backgrounds? - javascript

I need to mimic ranges/selections (those that highlight content on a website, and when you press for ex CTRL + C you copy the content) with divs as backgrounds. Chances are that the "highlighting divs" will be position:absolute;
<div id="highlight">
<!-- The highlightor divs would go here -->
</div>
<div id="edit">
<!-- The divs to be highlighted (that have text) would go here -->
</div>
Edit: Functionalities such as copying are essential.
PS: If you're curious about "why", refer to this question.
I created a new question because I felt the old one was pretty much answered, and this one differed to much from that one.

Here's the concept, with some code to get you started. Every time text is selected in the page, append that text to a hidden textarea on the page and then select the textarea. Then, wrap the original selection in a span to make it look selected. This way, you have the appearance of a selection, and since the hidden textarea is actually selected, when the user hits "ctrl+c" they are copying the text from the textarea.
To get the full functionality you are looking for, you'll probably want to extend this. Sniff keys for "ctrl+a" (for select all). I don't think you'll be able to override right-click behavior... at least not easily or elegantly. But this much is at least a proof of concept for you to run with.
window.onload = init;
function init()
{
document.getElementById("hidden").value = "";
document.body.ondblclick = interceptSelection;
document.body.onmouseup = interceptSelection;
}
function interceptSelection(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
var hidden = document.getElementById("hidden");
if (target == hidden) return;
var range, text;
if (window.getSelection)
{
var sel = getSelection();
if (sel.rangeCount == 0) return;
range = getSelection().getRangeAt(0);
}
else if (document.selection && document.selection.createRange)
{
range = document.selection.createRange();
}
text = "text" in range ? range.text : range.toString();
if (text)
{
if (range.surroundContents)
{
var span = document.createElement("span");
span.className = "selection";
range.surroundContents(span);
}
else if (range.pasteHTML)
{
range.pasteHTML("<span class=\"selection\">" + text + "</span>")
}
hidden.value += text;
}
hidden.select();
}
Here's the css I used in my test to hide the textarea and style the selected text:
#hidden
{
position: fixed;
top: -100%;
}
.selection
{
background-color: Highlight;
color: HighlightText;
}

Related

Disable tab key blur in focused contentEditable

I'm fully aware that this question looks like a duplicate, but it isn't. Please read.
I have a div tag with the attribute contentEditable="true". I'm trying to make it so I can use the tab key inside the div without it moving focuses. Here's some code I have that doesn't fully fix the problem:
var tab = function(id){
if(event.keyCode === 9){
event.preventDefault();
document.getElementById(id).innerHTML += " ";
setCaretPos(id);
}
}
var setCaretPos = function(id){
var node = document.getElementById(id);
node.focus();
var textNode = node.firstChild;
var caret = textNode.length;
var range = document.createRange();
range.setStart(textNode, caret);
range.setEnd(textNode, caret);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
That is basically just adding four spaces to the end of the value of the text inside the div, then moving the caret to the end. That becomes a problem when I use tabs on other lines, because it will just add the tab to the end (If I'm not already on the last line).
So, I want to insert the four spaces where the caret is. This is where the trouble comes. I'll show the code, but I'm not sure what I'm doing wrong. I want it to add the four spaces to the text before the caret, then add all the text after the caret. Here's the code:
var insertTabAtCaret = function(id){
if(event.keyCode === 9){
event.preventDefault();
var box = document.getElementById(id);
if(box.selectionStart || box.selectionStart == "0"){
var startPos = box.selectionStart;
var endPos = box.selectionEnd;
box.innerHTML.substring(0, startPos) + " " + box.innerHTML.substring(endPos, box.innerHTML.length);
}
}
}
Please help! Let me know how I can achieve tabbing in a contentEditable div using the method described above! (I would prefer it in normal JavaScript, but jQuery is permissible. Please, don't tell me about libraries, plugins, extensions, etc. I don't want to use any of those.)
document.querySelector("div").addEventListener("keydown",insertTabAtCaret);
function insertTabAtCaret(event){
if(event.keyCode === 9){
event.preventDefault();
var range = window.getSelection().getRangeAt(0);
var tabNode = document.createTextNode("\u00a0\u00a0\u00a0\u00a0");
range.insertNode(tabNode);
range.setStartAfter(tabNode);
range.setEndAfter(tabNode);
}
}
div#editor{
height: 200px;
width:80%;
border: solid
}
<div contenteditable id="editor">
some text
</div>

How to change the color of the text above a threshold length using js

Making a Twitter tweet box like div which is contenteditable is my task.
I want to change the text color of the div after 60 characters to be red. Something similar to what Twitter does after 120 characters.
I have so far been able to work out how to change the color of the extra text, but I am stuck up at a problem with cursor position.
Here is a fiddle to my attempted code: https://jsfiddle.net/Bharadwajdaya/36sn0dqn/1/
And a snippet below:
var red_string;
$('#div_editable').on("keyup", function(e) {
//console.log
var content = this.innerText;
var siz = this.innerText.length;
if (siz > 60) {
var contentEditableElement = document.getElementById("div_editable");
var con = this.innerText.substring(0, 60);
red_string = this.innerText.substring(60, siz);
var newString = "<span class='highlight'>" + red_string + "</span>";
console.log(newString + con);
$("#div_editable").html(con + newString);
document.getElementById("div_editable").focus();
setSelectionRange(document.getElementById("div_editable"), siz, siz);
setCaretToPos(siz - 61)
}
});
function setCaretToPos(siz) {
console.log(siz)
var node = document.getElementById("div_editable");
node.lastChild.focus();
var textNode = node.lastChild;
var caret = siz;
var range = document.createRange();
range.setStart(textNode, node.lastChild.innerText.length);
range.setEnd(textNode, node.lastChild.innerText.length);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
.highlight {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div id="div_editable" style="min-width: 320px; min-height: 120px" contenteditable="true">Use this place</div>
As you can see, applying the background color to extra text is working!
My problem is that once it reaches 60 characters the cursor or caret is moving back to start and the caret remains there and never expands.
Want to change the text color of the div after 60 to be red.Similar
like twitter
This is tricky, of course. As long as you just want to change the color of the text after a specified length (60 in your case), whatever you have attempted is good enough. Just wrap the exceeding characters in another element have a suitable class applied to it.
There is no problem with your code as such, and you are definitely on the right track.
Your Problem:
However, the trickiness starts compounding when you want it to be dynamically adjusted. i.e. to keep typing anywhere and get the extra text highlighted on the go.
The problem, which you then face is of keeping the cursor position intact!
Reason:
The contenteditable selection ranges and offsets restart for every element node in the contenteditable itself. And those work with text nodes only. This means, if in your contenteditable div there is another element then the cursor position would be offset from within that element.
This manifests in your case when you wrap the exceeding text into a span to highlight those. Once you overwrite the content of your div after wrapping the extra text in span, you lose the cursor position with the context of its text node.
Solution:
Before, making any changes cache the current cursor position along with the text node cursor is in. Then, after making changes (like wrapping extra text in another element and other stuff), use range.setStart and range.setEnd on that node's firstChild as that will be the text node you can work the cursor with.
You can find the current node by using the anchorNode of the selection object. This return the text node on which the cursor currently is. In order to use this later on in the script, you use parentNode to get its parent element. This element can then be used later while setting the cursor back. In the example below, I am using the em tag (like Twitter), which is then used to get to the actual text node by picking its firstChild. If the cursor is in the first 60 characters zone, then you will get the contenteditable div instead of em.
Here is a crude example snippet: (Works with Chrome, not tested with other browsers)
Notice, how you can now start typing from anywhere and the cursor is preserved.
var div = document.querySelector('div[contenteditable]'), allowedSize = 60;
reformat(div);
div.addEventListener('keyup', highlight);
function highlight(e) { reformat(e.target); }
function reformat(elem) {
var size = elem.textContent.length,
wrapper, txt, firstSection, nextSection, cursorPos
;
cursorPos = getCursorPosition();
elem.dataset.size = allowedSize - size;
if (size > allowedSize) {
txt = elem.textContent;
wrapper = document.createElement('em');
firstSection = txt.slice(0, allowedSize);
nextSection = txt.slice(allowedSize);
wrapper.textContent = nextSection;
elem.innerHTML = firstSection;
elem.appendChild(wrapper);
if (cursorPos) { setCursorPosition(cursorPos) }
}
}
function getCursorPosition() {
var retVal = {};
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount > 0) {
retVal.node = sel.anchorNode.parentNode;
retVal.pos = sel.getRangeAt(0).startOffset;
return retVal;
}
}
return null;
}
function setCursorPosition(position) {
var textNode, range, sel;
if (position.node.tagName == 'EM') {
textNode = div.getElementsByTagName('EM')[0].firstChild;
} else {
if (position.pos > allowedSize) {
textNode = div.getElementsByTagName('EM')[0].firstChild;
position.pos = 1;
} else { textNode = position.node.firstChild; }
}
range = document.createRange();
range.setStart(textNode, position.pos);
range.setEnd(textNode, position.pos);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
div[contenteditable] {
position: relative;
width: 70vw; height: 120px; padding: 4px;
margin: 8px auto; border: 1px solid #bbb;
font-family: sans-serif; outline: none;
}
div[contenteditable]::after {
content: attr(data-size);
position: absolute; top: 105%; right: 0;
}
div[contenteditable] em { background-color: #fcc; }
<div contenteditable="true" data-size="0">
Start typing anywhere in this box
</div>
Fiddle: https://jsfiddle.net/abhitalks/rmprz6rx/; for you to play with.
Note: The above example is a crude attempt. To make it cross-browser and polish off the kinks, is left as an exercise for the reader.
if you want to change text color of input . please follow below code
$('input').keypress(function(){
var len=$('input').val().length;
if(len>60)
{
$('input').css('color','red')
}
})
Thanks

Show button after selecting text for quoting

How to show a little image after selecting some text when we using the window.getSelection() code?
The most similar thing I found was this How can I position an element next to user text selection? but cant seem to get it to work properly :-s
Being kinda new to this also doesn't help.
Just need some simple code to do it, doesn't matter much where the image shows up, aslong as its near the selected text
-edit-
As I said in comment, the idea is to show a button (thought of image first but a button is better) floating near the selected text (after the selection is made), with link to quote what was selected, and if we clear what was selected the button doesn't show anymore.
And would this be possible by pulling mouse coords when finishing text selection and adding the x,y coords to the style of the button to be shown?
-edit-
got it working just like I wanted, having that coords idea in mind. Found this http://motyar.blogspot.pt/2010/02/get-user-selected-text-with-jquery-and.html and with it I came up with this:
function getSelected() {
if (window.getSelection) {
return window.getSelection();
}
else if (document.getSelection) {
return document.getSelection();
}
else {
var selection = document.selection && document.selection.createRange();
if (selection.text) {
return selection.text;
}
return false;
}
return false;
}
$(document).ready(function() {
var blank = '',
selectionImage;
$('#mensagem').mouseup(function(e) {
var selection = getSelected();
if (!selectionImage) {
selectionImage = $('<button>').attr({
type: 'button',
title: 'Citar Texto seleccionado',
id: 'quote-place'
}).html("Citar").css({
"color": "red"
}).hide();
$(document.body).append(selectionImage);
}
$("#quote-place").click(function quote() {
var txt = '';
if (window.getSelection) {
txt = window.getSelection();
}
else if (document.getSelection) {
txt = document.getSelection();
}
else if (document.selection) {
txt = document.selection.createRange().text;
}
else {
return;
}
document.aform.selectedtext.value = txt;
}).mousedown(function() {
if (selectionImage) {
selectionImage.fadeOut();
}
});
selectionImage.css({
top: e.pageY - 30,
//offsets
left: e.pageX - 13 //offsets
}).fadeIn();
});
});​
http://jsfiddle.net/ordhor/2Gc8c/
The problem now lies on when we click on the <div> without selecting text the button keeps showing. It should only appear when selecting text and I cant find how to fix it...
Let's suppose that image is the image element you want to insert. The idea is to place a place-holder <span> element after the selected text, since we can't compute the position of text nodes. So, we start with this CSS:
.place-holder {position: relative;}
.quote-image {
position: absolute;
top: 0;
left: 0;
}
The place-holder class is for our <span> element, and the position property for the image we want to put in, which is absolutely positioned in order to keep a zero-width <span>.
Now we want to check whether a selection is made or not. Unfortunately, there's no standard event raised when a selection is made. The onselect event is fired only if a selection is made in text fields, and not even when the selection is canceled.
But since the selection is usually made using mouse and keyboard events, we can listen to them and check if a selection is made.
var prevRng;
function checkSelection() {
var s = getSelection(), r = s.rangeCount && s.getRangeAt(0);
if (prevRng) {
if (!r || r.endContainer !== prevRng.endContainer
|| r.endOffset !== prevRng.endOffset) {
// The selection has changed or been cleared
selectionClear();
}
}
if (r) setPlaceHolder(r);
}
document.addEventListener("mouseup", checkSelection);
// mousedown usually clears the selection
document.addEventListener("mousedown", checkSelection);
// You can use the keyboard to extend the selection, or select all (Ctrl+A)
document.addEventListener("keyup", checkSelection);
prevRng is a variable in the function scope to store the selection made. Now we can make our working code:
function setPlaceHolder(range) {
if (range.startContainer === range.endContainer
&& range.startOffset === range.endOffset) {
// The selection is clear
prevRng = null;
return;
}
prevRng = range;
var endc = range.endContainer, span = document.createElement("span");
span.className = "place-holder";
span.appendChild(image);
if (endc.nodeType === Node.TEXT_NODE) { // Node.TEXT_NODE === 3
var p1 = endc.nodeValue.substring(0, range.endOffset),
p2 = endc.nodeValue.substring(range.endOffset);
endc.nodeValue = p1;
if (p2)
endc.parentNode.insertBefore(document.createTextNode(p2),
endc.nextSibling);
}
endc.parentNode.insertBefore(image, endc.nextSibling);
}
function selectionClear() {
if (!prevRng) return;
var endc = prevRng.endContainer;
if (endc.nextSibling.className === "place-holder") {
endc.parentNode.removeChild(endc.nextSibling);
if (endc.nodeType === Node.TEXT_NODE
&& endc.nextSibling.nodeType === Node.TEXT_NODE) {
// Joining previously divided text nodes
endc.nodeValue += endc.nextSibling.nodeValue;
endc.parentNode.removeChild(endc.nextSibling);
}
}
}
Edit: it seems I've misunderstood your question. I thought you wanted to insert the image after the selection... So, you want to know when the selection is actually made?
Edit 2: changed more or less everything to match the request.
For example:
$("div.rte").on("mouseup",function(){ // event binding on the div
if(window.getSelection().toString().length>0) //check length if >0 then only proceed
{
// do processing
}
this should resolve your problem from my understanding of the problem
I have been trying to implement similar functionality , my problem is that the document can be edited as well.
The way of saving the annotation is a challenge, currently Im thinking to save the start and end index of the annotion in the db, when document content changes , using mutation observers , I will calculate the new char count of the annotation and then save again in db
Please let me know are there any other ways of storing and retrieving from db

How can I possibly accomplish this Javascript highlighting technique?

<div id="content" contenteditable="false" style="margin:-0.2em 1em; font-family:New Century Schoolbook; font-size:20; color:black; line-height:170%">
<span style="margin-left:1.7em">Jenny was cute!</span>
<span style="margin-left:1.7em">She looked happy with the instructor.</span>
<span style="margin-left:1.7em">Can we meet her again?</span><br>
<span style="margin-left:18em">Sakura</span>
</div>
Here is some HTML I have created to match a client's needs. The margin-left between each one is so that I can insert a separate image in between each sentence (user selection turns completely weird when images are in the HTML). The user needs to be able to select, highlight, and add memos to sections like in the iBook app. However, unlike the iBook app, it needs to happen with formatted text. So it needs to be selectable, editable, AND support bold, italic, and underline in the same view. Here is the Javascript I have come up with (the HTML and the result from the Javascript are not standards compliant, but I don't care since this is not going to be a webpage and I'm fed up):
function addHighlight(className, id)
{
var html = '';
var sel = window.getSelection().getRangeAt(0);
var container = document.createElement('JimSpan');
container.id = id;
container.className = className;
container.appendChild(sel.extractContents());
var arrElements = document.getElementsByTagName('span');
for(i = 0; i < arrElements.length-1; i++)
{
if(!arrElements[i].innerText.match(/\S/))
{
arrElements[i].parentNode.removeChild(arrElements[i]);
}
}
sel.insertNode(container);
return 'js: ADDED HIGHLIGHT';
}
The reason I have to use "JimSpan" and not "Span" is because the user ALSO needs to be able to delete the notes while the page is not displaying, which means I have to edit it manually with regular expressions. Since there are tons of spans I will have no idea when mine actually ends. This works well with one exception. If the user selects all of one element and part of another, the result is this:
<div id="content" contenteditable="false" style="margin:-0.2em 1em; font-family:New Century Schoolbook; font-size:20; color:black; line-height:170%">
<jimspan id="memo0" class="pinkHilite"><span style="margin-left:1.7em">Jenny was cute!</span>
<span style="margin-left:1.7em">She</span></jimspan><span style="margin-left:1.7em"> looked happy with the instructor.</span>
<span style="margin-left:1.7em">Can we meet her again?</span><br>
<span style="margin-left:18em">Sakura</span>
</div>
As you can see in the middle, the split resulted in the sentence She looked happy with the instructor being split into two spans, each with the margin-left, and I am left with a huge gap in the middle of the sentence. I will accept either of the following:
1) A way for me to produce the following (technically invalid, but displays fine) HTML (I don't mind editing the current range with regex if there is a way to do that):
<div id="content" contenteditable="false" style="margin:-0.2em 1em; font-family:New Century Schoolbook; font-size:20; color:black; line-height:170%">
<jimspan id="memo0" id="pinkHilite"><span style="margin-left:1.7em">Jenny was cute!</span>
<span style="margin-left:1.7em">She</jimspan> looked happy with the instructor.</span>
<span style="margin-left:1.7em">Can we meet her again?</span><br>
<span style="margin-left:18em">Sakura</span>
</div>
2) A way to force the user selection to only encompass the inside of 1 span (the selection must change, not just the affected highlight region).
Please help save my sanity -__-;
Option 2 is the easiest solution, and quite achievable using Range and Selection objects (ruling out IE < 9, which doesn't matter for a UIWebView but could to someone wanting to do similar in a browser). The following will limit the selection to the element containing the point at which the user started selecting (note: for the sake of illustration, it only works with mouse-based selection):
Live demo: http://jsfiddle.net/U4smu/
Code:
function isOrIsDescendantOf(node, ancestor) {
while (node) {
if (node === ancestor) {
return true;
}
node = node.parentNode;
}
}
function selectionIsBackwards(sel) {
var range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
var backwards = range.collapsed;
range.detach();
return backwards;
}
document.onmouseup = function() {
var sel = window.getSelection();
var containerEl = sel.anchorNode;
if (containerEl ) {
if (containerEl .nodeType == 3) {
containerEl = containerEl.parentNode;
}
if (!isOrIsDescendantOf(sel.focusNode, containerEl)) {
var backwards = selectionIsBackwards(sel);
var range = document.createRange();
if (backwards) {
range.setStart(containerEl, 0);
range.setEnd(sel.anchorNode, sel.anchorOffset);
} else {
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(containerEl, containerEl.childNodes.length);
}
sel.removeAllRanges();
sel.addRange(range);
}
}
};

Tag-like autocompletion and caret/cursor movement in contenteditable elements

I'm working on a jQuery plugin that will allow you to do #username style tags, like Facebook does in their status update input box.
My problem is, that even after hours of researching and experimenting, it seems REALLY hard to simply move the caret. I've managed to inject the <a> tag with someone's name, but placing the caret after it seems like rocket science, specially if it's supposed work in all browsers.
And I haven't even looked into replacing the typed #username text with the tag yet, rather than just injecting it as I'm doing right now... lol
There's a ton of questions about working with contenteditable here on Stack Overflow, and I think I've read all of them, but they don't really cover properly what I need. So any more information anyone can provide would be great :)
You could use my Rangy library, which attempts with some success to normalize browser range and selection implementations. If you've managed to insert the <a> as you say and you've got it in a variable called aElement, you can do the following:
var range = rangy.createRange();
range.setStartAfter(aElement);
range.collapse(true);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);
I got interested in this, so I've written the starting point for a full solution. The following uses my Rangy library with its selection save/restore module to save and restore the selection and normalize cross browser issues. It surrounds all matching text (#whatever in this case) with a link element and positions the selection where it had been previously. This is triggered after there has been no keyboard activity for one second. It should be quite reusable.
function createLink(matchedTextNode) {
var el = document.createElement("a");
el.style.backgroundColor = "yellow";
el.style.padding = "2px";
el.contentEditable = false;
var matchedName = matchedTextNode.data.slice(1); // Remove the leading #
el.href = "http://www.example.com/?name=" + matchedName;
matchedTextNode.data = matchedName;
el.appendChild(matchedTextNode);
return el;
}
function shouldLinkifyContents(el) {
return el.tagName != "A";
}
function surroundInElement(el, regex, surrounderCreateFunc, shouldSurroundFunc) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1 && shouldSurroundFunc(el)) {
surroundInElement(child, regex, surrounderCreateFunc, shouldSurroundFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
function updateLinks() {
var el = document.getElementById("editable");
var savedSelection = rangy.saveSelection();
surroundInElement(el, /#\w+/, createLink, shouldLinkifyContents);
rangy.restoreSelection(savedSelection);
}
var keyTimer = null, keyDelay = 1000;
function keyUpLinkifyHandler() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateLinks();
keyTimer = null;
}, keyDelay);
}
HTML:
<p contenteditable="true" id="editable" onkeyup="keyUpLinkifyHandler()">
Some editable content for #someone or other
</p>
As you say you can already insert an tag at the caret, I'm going to start from there. The first thing to do is to give your tag an id when you insert it. You should then have something like this:
<div contenteditable='true' id='status'>I went shopping with <a href='#' id='atagid'>Jane</a></div>
Here is a function that should place the cursor just after the tag.
function setCursorAfterA()
{
var atag = document.getElementById("atagid");
var parentdiv = document.getElementById("status");
var range,selection;
if(window.getSelection) //FF,Chrome,Opera,Safari,IE9+
{
parentdiv.appendChild(document.createTextNode(""));//FF wont allow cursor to be placed directly between <a> tag and the end of the div, so a space is added at the end (this can be trimmed later)
range = document.createRange();//create range object (like an invisible selection)
range.setEndAfter(atag);//set end of range selection to just after the <a> tag
range.setStartAfter(atag);//set start of range selection to just after the <a> tag
selection = window.getSelection();//get selection object (list of current selections/ranges)
selection.removeAllRanges();//remove any current selections (FF can have more than one)
parentdiv.focus();//Focuses contenteditable div (necessary for opera)
selection.addRange(range);//add our range object to the selection list (make our range visible)
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createRange();//create a "Text Range" object (like an invisible selection)
range.moveToElementText(atag);//select the contents of the a tag (i.e. "Jane")
range.collapse(false);//collapse selection to end of range (between "e" and "</a>").
while(range.parentElement() == atag)//while ranges cursor is still inside <a> tag
{
range.move("character",1);//move cursor 1 character to the right
}
range.move("character",-1);//move cursor 1 character to the left
range.select()//move the actual cursor to the position of the ranges cursor
}
/*OPTIONAL:
atag.id = ""; //remove id from a tag
*/
}
EDIT:
Tested and fixed script. It definitely works in IE6, chrome 8, firefox 4, and opera 11. Don't have other browsers on hand to test, but it doesn't use any functions that have changed recently so it should work in anything that supports contenteditable.
This button is handy for testing:
<input type='button' onclick='setCursorAfterA()' value='Place Cursor After <a/> tag' >
Nico

Categories

Resources