我正在制作一个类似facebook中的提及系统,现在我用textarea实现了它,但现在我想让它与一个可信的div一起工作,这样我就可以格式化文本。
现在,当你按下“@”和一个字母在数组中搜索包含该字母的结果时,使用JQuery-UI自动完成功能,当你选择结果时它会在div中显示结果,但现在我要强调像facebook这样的提及,所以我提出了一个:
<b style="background: #somecolor"></b>
所以提到的看起来像这样:
<b style="background: #somecolor">Mention</b>
到目前为止一直很好,但是当我想继续写作时,我写的所有新文本都在<b></b>
里面,如下所示:
<b style="background: #somecolor">Mention all my new text</b>
而不是<b style="background: #somecolor">Mention</b> all my new text
所有新文本都被突出显示,这是一张图片:
http://dev.frinki.com/question/1.png
所以这是使用
的javascript代码$("#Conversacion").bind("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB && $(this).data("autocomplete").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
var term = request.term,
results = [];
console.log(term);
if (term.indexOf("@") >= 0) {
term = extractLast(request.term);
if (term.length > 0) {
results = $.ui.autocomplete.filter(availableTagsFollowing, term);
} else {
//results = ['Start typing...'];
}
}
response(results);
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.innerHTML);
// remove the current input
terms.pop();
// add the selected item
terms.push('<b style="background:#E0FFC5">' + ui.item.label + '</b>');
this.innerHTML = terms.join("");
$('#mentionsHidden').val($('#mentionsHidden').val() + '@['+ui.item.value+':'+ui.item.label+']');
mentionsString = $('#mentionsHidden').val();
return false;
}
});
这是HTML代码
<div name="Descripcion" id="Conversacion" class="Conversacion" contenteditable="true"></div>
这是Chrome's Inspector的图片:
http://dev.frinki.com/question/2.png
我在facebook上查看它是如何完成的,这是在facebook上的chrome检查员的照片:
3.png(其他两张图片的域名和文件夹相同,对不起,这是一个新用户,我不能发布超过2个链接)
当您选择要提及的人时,Facebook似乎会在其使用的范围中创建一个新行,以便将文本与<b></b>
分开。
我希望有人可以帮助我。
顺便说一下,我很害怕我的英语,我来自南美洲。
答案 0 :(得分:1)
我只是在解释/总结您的想法:您的div
是可编辑的,用户可以输入它。在某些情况下,您将显示用户可以选择的列表。选择完成后,div
的内容将被修改为包含某个包装元素中的选定值。之后用户输入但是字符被插入到包装器中而不是在包装器之后,对吧?
不需要的行为的原因是焦点位置。从技术上讲,有一系列选定的内容会被用户输入的字符所取代。此范围通常是不可见的,因为它跨越0个字符。当使用鼠标选择文本时,它会覆盖一些内容(可能还有多个元素)。
现在让我们假设以下HTML:<b>One</b><b>Two</>
。可能是空范围介于b
标签之间。当输入下一个字符 A 时,需要将其插入文档中 - 但即使已知范围/插入符/光标位置,也有3个可能的目的地:
b
:<b>OneA</b><b>Two</b>
<b>One</b>A<b>Two</b>
b
的开头:<b>One</b><b>ATwo</b>
您的浏览器会选择第一种方法:粗略地说,要继续添加最近添加的元素。
现在,有a pretty complex range system指定用于放置新内容的方法。有关示例,请参阅Introduction to Range和a question on setting the range。
解决问题的一般想法是在新插入的内容之后创建一些空内容。因此最终得到<b style="background: #somecolor">Mention</b>|
。请注意,您需要放置一个空文本节点,而不是管道字符 | ,而不能在HTML代码中显示。 DOM能够处理超过序列化的HTML / HTML代码!然后,您需要选择空文本节点的内容。然后,下一个字符将替换空文本节点(选择文本节点内的空内容的范围将被替换,因此字符将被插入到文本节点中以更精确)。假设输入的字符为 A ,则效果最终为<b style="background: #somecolor">Mention</b>A
。
为了实现我建议的想法:
innerHTML
并使用DOM方法。 innerHTML
无法创建空文本节点。我准备了一个jsFiddle来展示一个基本的实现。请注意,这是一个快速入侵,既不支持跨多个元素也不支持空父项。该示例将所有大写字母包装在b
标记中,该标记应足以让您入门。
// Get the current selection and range
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var textNode = range.startContainer;
var text = textNode.data;
// Create a new node to hold the text in front of the range
var textBeforeAddedContent = document.createTextNode(text.substr(0, range.startOffset));
// Update current node to remove everything which goes into the just create node and the content to be replaced
textNode.data = text.substr(range.endOffset);
// Put stuff in front of the range into the document. Unless a non-empty selection was made the content looks now exactly as before calling this code
textNode.parentNode.insertBefore(textBeforeAddedContent, textNode);
// Place the new content in between the text nodes
var wrapper = document.createElement('b');
wrapper.appendChild(document.createTextNode(letter));
textNode.parentNode.insertBefore(wrapper, textNode);
// You might want to set the new range explicitly here and add an empty text node wherever newly typed letters shall go
// Set the new selection explicitly
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
selection.addRange(range);
Chrome无法从偏移量0开始进行零长度选择,而是将零长度选择设置为前一个孩子的结尾。支持Chrome的丑陋黑客可能是插入一些不可见的字符,并在插入下一个字符后将其删除。
对于原始问题,Chrome的问题不应该太重要,因为在包装元素之后插入空格可能是有效的。然后,范围的起始偏移量将设置为1,以在空格后插入新内容。
答案 1 :(得分:0)
我已经迟了几年了,但是考虑到它并没有像你找到一个强大的,跨浏览器兼容的解决方案来解决你的问题,我想我会发出声音英寸
简单地说,您不需要偏离textarea
实施,以实现您的目标。
您可以简单地使用div
,尺寸和位置相同,并放置在textarea
下,其中包含textarea
内容的版本,每个内容的字符串都包含在内在span
元素中。您可以通过这些跨度格式化提及字符串。为了给这个元素对提供单个元素的外观,textarea
的背景,以及&#34; shadow div&#34;的文本,边框和轮廓。应该透明。
如果您想了解如何在现有插件中完成此操作,请查看Mentionator,该插件提供您尝试模拟的功能。它结构合理,易于理解,并且有很多评论,所以你应该很少理解如何采用这种技术。它也是你的真正维护:)。