我正在我的网站上开发一个contentEditable区域,用户可以在其中相互键入消息。
<div contentEditable="true" class="smartText">User types here...</div>
问题是,我们内部会有智能文字,这意味着如果用户在此div中键入@usersame
,如果用户名存在,则@username
应以蓝色突出显示,如果用户名存在,则应以绿色突出显示存在。当然,所有这些都应该在用户输入时发生......
我不知道从哪里开始,现在我有这个:
$("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);
});
但是文本不断跳跃(以及插入位置)并且感觉不是正确的方向。我想这与JSFiddle有点相似,它会在找到它时为代码着色。 我基本上想要和Twitter一样。
这是一个可以使用的JSFiddle:http://jsfiddle.net/denislexic/bhu9N/4/
提前感谢您的帮助。
答案 0 :(得分:13)
我喜欢这个问题而且我非常努力地解决这个问题。我相信我终于成功了(只需要一点帮助)。
一段代码:
[...]
// formatText
formatText: function (el) {
var savedSel = helper.saveSelection(el);
el.innerHTML = el.innerHTML.replace(/<span[\s\S]*?>([\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("");
}
}
<强>截图:强>
JSFiddle here: http://jsfiddle.net/hayatbiralem/9Z3Rg/11/
需要的外部资源:
帮助者问题(谢谢): replace innerHTML in contenteditable div
正则表达式测试工具(谢谢): http://www.pagecolumn.com/tool/regtest.htm
答案 1 :(得分:4)
请注意,用户输入的HTML标记可能非常令人惊讶,例如:<span>@use</span><span>rname</span>
,对用户来说仍然是@username
。
为避免contentEditable
元素中的疯狂插入符号行为(以及其他一些令人讨厌的副作用),每次HTML发生更改时都应使用W3C DOM API并遍历DOM树(您可以通过在计时器上轮询body.innerHTML
来嗅探更改。
我最近为CKEditor回答了similar question,并描述了如何构建DOM的文本到节点映射的算法,以查找文本匹配。 CKEditor DOM API与W3C非常相似,您可以采用相同的算法。
找到匹配后,您应该使用DOM Range API来操纵DOM节点的内容。例如,用样式<SPAN>
包装一行纯文本:
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代码页面的东西,需要在这里回答。
答案 2 :(得分:2)
这似乎可以解决您的问题。
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, '<span class="green">'+lastword+'</span>');
$this.html(orgtext);
}
}
}
});
});
文本突出显示后,光标将转到div的开头。所以这仍然需要修复。如果我能找到它,我将更新解决方案。与此同时,只需玩弄我现在提供的内容,看看它是否有帮助。
答案 3 :(得分:1)
正如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 "<span class='exists'>" + username + "</span>";
});
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
s(see this GitHub issue for Lettering.js for an example)中包含某些内容时的可访问性问题。
编辑:另请注意,这不是一个强大的解决方案(例如,它不会对复制粘贴做出反应)。 YMMV。
答案 4 :(得分:0)
您使用的方法似乎非常耗费浏览器,如果有人非常快速地输入并且在通过ajax验证“String”之前它正在运行多个请求,则可能会导致一些问题。如果你使用像http://aehlke.github.io/tag-it/这样的库,你可能会更好 - 你可以描述一个改变字体颜色等的功能,就像它推荐一个标签一样。
如果我有时间,我会做小提琴演示。