在contenteditable div中replaceinnerHTML

我需要在contenteditable div中实现突出显示的数字(将来我会添加更复杂的规则)。 问题是,当我插入新的内容与JavaScriptreplace,DOM的变化和contenteditable div失去了重点。 我需要的是在目前的位置上注意div与脱字符号,所以用户可以input没有任何问题,我的function简单地突出显示数字。 谷歌search我决定Rangy图书馆是最好的解决scheme。 我有以下代码:

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

问题是function结束后工作重点回到div,但是caret总是指向div开始,我可以在任何地方input,exe开始。 此外console.logtypes以下Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection. Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection. 请帮我实现这个function。 我开放另一个解决scheme,不仅是rangy图书馆。 谢谢!

http://jsfiddle.net/2rTA5/这是jsfiddle,但它不工作正常(当我键入数字到我的div没有任何反应),不知道也许是我(第一次通过jsfiddle发布代码)或资源不支持contenteditable。 UPD *我阅读在stackoverflow类似的问题,但解决scheme不适合我的情况:(

问题在于Rangy的保存/恢复select模块的工作方式是将不可见的标记元素插入到select边界的DOM中,然后您的代码将所有HTML标记(包括Rangy的标记元素(如错误消息所示))去除。 你有两个select:

  1. 移动到DOM遍历解决scheme来着色数字而不是innerHTML 。 这将是更可靠,但更多的参与。
  2. 实现一个替代字符索引的select保存和恢复。 这将是一般脆弱,但会做你想在这种情况下。

UPDATE

我敲了一个基于字符索引的select保存/恢复Rangy(上面的选项2)。 这有点粗糙,但它为这个案件做了工作。 它通过遍历文本节点来工作。 我可以以某种forms将它添加到Rangy中。 ( 更新2012年6月5日: 我现在已经以更可靠的方式实现了Rangy。 )

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

码:

 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); } 

我想感谢Tim在这里与我们分享的function,这对我正在进行的一个项目非常重要。 我embedded他的function一个小的jQuery插件,可以在这里访问: https : //jsfiddle.net/sh5tboL8/

 $.fn.get_selection_start = function(){ var result = this.get(0).selectionStart; if (typeof(result) == 'undefined') result = this.get_selection_range().selection_start; return result; } $.fn.get_selection_end = function(){ var result = this.get(0).selectionEnd; if (typeof(result) == 'undefined') result = this.get_selection_range().selection_end; return result; } $.fn_get_selected_text = function(){ var value = this.get(0).value; if (typeof(value) == 'undefined'){ var result = this.get_selection_range().selected_text; }else{ var result = value.substring(this.selectionStart, this.selectionEnd); } return result; } $.fn.get_selection_range = function(){ var range = window.getSelection().getRangeAt(0); var cloned_range = range.cloneRange(); cloned_range.selectNodeContents(this.get(0)); cloned_range.setEnd(range.startContainer, range.startOffset); var selection_start = cloned_range.toString().length; var selected_text = range.toString(); var selection_end = selection_start + selected_text.length; var result = { selection_start: selection_start, selection_end: selection_end, selected_text: selected_text } return result; } $.fn.set_selection = function(selection_start, selection_end){ var target_element = this.get(0); selection_start = selection_start || 0; if (typeof(target_element.selectionStart) == 'undefined'){ if (typeof(selection_end) == 'undefined') selection_end = target_element.innerHTML.length; var character_index = 0; var range = document.createRange(); range.setStart(target_element, 0); range.collapse(true); var node_stack = [target_element]; var node = null; var start_found = false; var stop = false; while (!stop && (node = node_stack.pop())) { if (node.nodeType == 3){ var next_character_index = character_index + node.length; if (!start_found && selection_start >= character_index && selection_start <= next_character_index){ range.setStart(node, selection_start - character_index); start_found = true; } if (start_found && selection_end >= character_index && selection_end <= next_character_index){ range.setEnd(node, selection_end - character_index); stop = true; } character_index = next_character_index; }else{ var child_counter = node.childNodes.length; while (child_counter --){ node_stack.push(node.childNodes[child_counter]); } } } var selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); }else{ if (typeof(selection_end) == 'undefined') selection_end = target_element.value.length; target_element.focus(); target_element.selectionStart = selection_start; target_element.selectionEnd = selection_end; } } 

插件只做我需要做的,获得选定的文本,并设置自定义文本select。 它也适用于文本框和contentEditable divs。