Replace Innerhtml in Contenteditable Div

replace innerHTML in contenteditable div

The problem is that Rangy's save/restore selection module works by inserting invisible marker elements into the DOM where the selection boundaries are and then your code strips out all HTML tags, including Rangy's marker elements (as the error message suggests). You have two options:

  1. Move to a DOM traversal solution for colouring the numbers rather than innerHTML. This will be more reliable but more involved.
  2. Implement an alternative character index-based selection save and restore. This would be generally fragile but will do what you want in this case.

UPDATE

I've knocked up a character index-based selection save/restore for Rangy (option 2 above). It's a little rough, but it does the job for this case. It works by traversing text nodes. I may add this into Rangy in some form. (UPDATE 5 June 2012: I've now implemented this, in a more reliable way, for Rangy.)

jsFiddle: http://jsfiddle.net/2rTA5/2/

Code:

function saveSelection(containerEl) {
var charIndex = 0, start = 0, end = 0, foundStart = false, stop = {};
var sel = rangy.getSelection(), range;

function traverseTextNodes(node, range) {
if (node.nodeType == 3) {
if (!foundStart && node == range.startContainer) {
start = charIndex + range.startOffset;
foundStart = true;
}
if (foundStart && node == range.endContainer) {
end = charIndex + range.endOffset;
throw stop;
}
charIndex += node.length;
} else {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
traverseTextNodes(node.childNodes[i], range);
}
}
}

if (sel.rangeCount) {
try {
traverseTextNodes(containerEl, sel.getRangeAt(0));
} catch (ex) {
if (ex != stop) {
throw ex;
}
}
}

return {
start: start,
end: end
};
}

function restoreSelection(containerEl, savedSel) {
var charIndex = 0, range = rangy.createRange(), foundStart = false, stop = {};
range.collapseToPoint(containerEl, 0);

function traverseTextNodes(node) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
throw stop;
}
charIndex = nextCharIndex;
} else {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
traverseTextNodes(node.childNodes[i]);
}
}
}

try {
traverseTextNodes(containerEl);
} catch (ex) {
if (ex == stop) {
rangy.getSelection().setSingleRange(range);
} else {
throw ex;
}
}
}

function formatText() {
var el = document.getElementById('pad');
var savedSel = saveSelection(el);
el.innerHTML = el.innerHTML.replace(/(<([^>]+)>)/ig,"");
el.innerHTML = el.innerHTML.replace(/([0-9])/ig,"<font color='red'>$1</font>");

// Restore the original selection
restoreSelection(el, savedSel);
}

contentEditable div replace innerHTML on the fly

If you're replacing the HTML, you will need some means of saving and restoring your selection. The options are:

  1. Insert elements with IDs at the start and end of the selection before the HTML replacement, retrieve them by ID afterwards and move the selection boundaries using those elements. This is the most robust method and is what Rangy does.
  2. Store some kind of character-based offset for the start and end of the selection before the HTML replacement and recreate the selection afterwards. This has all kinds of problems in the general case but may be good enough, depending on your needs. I've posted functions on SO to do this before that depend on Rangy, but you could easily strip out the Rangy stuff if you don't mind losing support for IE < 9.
  3. Store and restore selection boundaries by pixel coordinates. Browser support for this is not good and will not be suitable if your HTML replacement changes the layout.

Modifying innerHTML in a contentEditable=true DIV causes loss of focus or wrong selection

Focus is being lost because clean2() manipulates the div. In FF the caret gets reset to the beginning of the editable div for the same reason.
You can force the focus to stay by manually calling el.focus() in clean2(), however this will not fix the caret issue.
You can set the caret position with something like:

window.getSelection().removeAllRanges();
var range = document.createRange();
range.setStart(el,start)
range.setEnd(el,start);
window.getSelection().addRange(range);

However, this wont work for you because the "el" you need to set the cursor position in is the newly created text node. Unfortunately, the type of syntax highlighting you are trying to do is very complicated. If you wish to pursue it, check out:

Set cursor position on contentEditable <div>

Get caret position in contentEditable div



Related Topics



Leave a reply



Submit