Jquery / Javascript – 在contentEditable区域中突出显示为用户类型的语法

我正在我的网站上开发一个contentEditable区域,用户可以在其中相互键入消息。

User types here...

问题是,我们内部会有智能文本,这意味着如果用户在此div中键入@usersame ,如果用户名存在,则@username应以蓝色突出显示,如果不存在,则应以绿色突出显示。 当然,所有这一切都应该在用户输入时发生……

我不知道从哪里开始,现在我有这个:

 $("body").on("keyup",".smartText",function(){ var $this = $(this), value = $this.html(), regex = /[^>]#\S+[^ ]/gim; value = value.replace(regex,"$&"); $this.html(value); }); 

但是文本不断跳跃(以及插入位置)并且感觉不是正确的方向。 我想这与JSFiddle有点相似,它会在找到它时为代码着色。 我基本上想要和Twitter一样。

这是一个可以玩的JSFiddle: http : //jsfiddle.net/denislexic/bhu9N/4/

在此先感谢您的帮助。

我喜欢这个问题而且我很努力地解决了。 我相信我终于成功了(有一点帮助)。

= UPDATED =

一段代码:

 [...] // formatText formatText: function (el) { var savedSel = helper.saveSelection(el); el.innerHTML = el.innerHTML.replace(/([\s\S]*?)<\/span>/g,"$1"); el.innerHTML = el.innerHTML.replace(/(@[^\s<\.]+)/g, helper.highlight); // Restore the original selection helper.restoreSelection(el, savedSel); } [...] // point keyup: function(e){ // format if key is valid if(helper.keyIsAvailable(e)){ helper.formatText($this[0]); } // delete blank html elements if(helper.keyIsDelete && $this.text()=="") { $this.html(""); } } 

截图:

sCREENSHOT 2

JSFiddle: http //jsfiddle.net/hayatbiralem/9Z3Rg/11/

需要的外部资源:

助手问题(谢谢): 在contenteditable div中替换innerHTML

正则表达式测试工具(谢谢): http //www.pagecolumn.com/tool/regtest.htm

请记住,用户键入的HTML标记可能非常令人惊讶,例如: @username ,它仍然看起来像用户的@username

为了避免在contentEditable元素中出现疯狂的插入符号行为(以及其他一些令人讨厌的副作用),你应该使用W3C DOM API并在每次HTML发生变化时遍历DOM树(你可以通过轮询body.innerHTML来嗅探更改)一个计时器)。

我最近回答了一个类似的CKEditor 问题 ,并描述了如何构建DOM的文本到节点映射的算法,以找到文本匹配。 CKEditor DOM API与W3C非常相似,您可以采用相同的算法。

找到匹配后,您应该使用DOM Range API来操作DOM节点的内容。 例如,使用样式化的包装一行纯文本:

 var range = document.createRange(); range.setStart(startNode, startOffset); range.setEnd(endNode, endOffset); var span = document.createElement("span"); span.style.backgroundColor = "blue" range.surroundContents(span); 

总的来说,这个任务非常重要,当然不是一个可以放入JavaScript代码页面的东西,需要在这里回答。

这似乎可以解决您的问题。

DEMO: http : //jsfiddle.net/bhu9N/5/

 $(document).ready(function() { $("body").on("keyup", ".editable", function(e) { var $this = $(this); if(e.keyCode==32) {//space var words = $this.text().split(' '); var lastword = $.trim(words[words.length-1]); var reg = /^@\S+/; if(reg.test(lastword)) { //make an AJAX call for checking this word for existence //suppose data is returned and data==1 means green var data = 1; if(data==1) { var orgtext = $this.html(); orgtext = orgtext.replace(lastword, ''+lastword+''); $this.html(orgtext); } } } }); });​ 

文本突出显示后,光标将转到div的开头。 所以这仍然需要修复。 如果我能找到它,我会更新解决方案。 与此同时,只需玩弄我现在提供的内容,看看它是否有帮助。

正如Ezos在他的回答中指出的那样,每次用户释放密钥时,我都不建议尝试做任何密集的事情(比如让Ajax请求检查是否存在用户名)。 你可能会遇到糟糕的时间。 话虽如此,我建议在用户停止输入之后等待一段时间来运行他们输入的内容并突出显示单词,例如:

 var textarea = $(".smartText"); var highlightWords = function highlightWords() { var original = textarea.text(); var replaced = original.replace(/@[a-zA-Z0-9]+/g, function (username) { // Magic return "" + username + ""; }); textarea.html(replaced); }; var timer; textarea.keyup(function (e) { clearTimeout(timer); if ($(this).text()) { timer = setTimeout(highlightWords, 1000); } }); 

链接到小提琴: http : //jsfiddle.net/neJLW/

我认为上面的代码应该让你开始朝着正确的方向前进。 就像你说的那样,光标仍会跳转,所以你每次编辑div的内容时都必须保存它并将其重置在旧位置。 此外,您需要根据预期确定是否存在用户名的时间来调整超时。 您需要使用用户名检查替换// Magic并相应地调整返回值。

顺便说一下,你需要记住在span包装某些东西的可访问性问题(例如, 参见Lettering.js的GitHub问题 )。

编辑:另请注意,这不是一个强大的解决方案(例如,它不会复制粘贴)。 因人而异。

您使用的方法似乎非常耗费浏览器,如果有人非常快速地输入并且在通过ajaxvalidation“String”之前它正在运行多个请求,则可能会导致一些问题。 如果使用http://aehlke.github.io/tag-it/这样的库,可能会更好 – 您可以像修改标签一样描述更改字体颜色等function。

如果我有时间,我会做小提琴演示。