JavaScript实现@提及好友功能:兼容IE与Firefox的完整方案

作者:白露时间:2026-10-30 00:25:21浏览:402

本文详细介绍如何使用JavaScript在输入框中实现@提及好友的功能,重点解决IE与Firefox的兼容性问题。通过监听mousedown事件捕获@符号,利用DOM Range对象插入自定义span标签,并结合光标位置动态定位弹出菜单。文章提供完整代码示例,涵盖Shift+@输入、Backspace删除及鼠标悬停三种触发场景,确保在IE6至Firefox各版本中稳定运行。

功能需求与兼容性要求

功能需求

1. 当用户输入@符号时,自动弹出匹配的好友列表菜单

2. 当鼠标光标移入包含已@好友的标签区域时,显示关联菜单

3. 当按下Backspace键删除时,若光标前存在包含已@好友的标签,则弹出对应菜单

4. 确保功能在IE和Firefox浏览器中均能正常运行

@符号输入与菜单定位实现

具体实现方案

针对上述第一个需求,直观的做法是对输入框绑定相关事件。这里需要绑定的是mousedown事件,而非mouseup。原因在于,如果绑定mouseup事件,即使使用event.preventDefault()也无法阻止键盘输入@符号。此外,在事件回调中直接使用return false同样无法达到预期效果。

绑定mousedown事件后,下一步是插入自定义的、包含@好友信息的标签。新浪微博的输入框采用textarea元素,难以直接获取其内部处理逻辑,因此参考了百度贴吧的实现方式。

功能需求与兼容性要求

观察发现,贴吧通过插入<span class='at'></span>标签来实现。这种做法便于后端使用正则表达式进行匹配处理。

具体实现逻辑如下

核心代码如下

vm.check_key=function(e){
var editor=$('editor'),range;
if(e.shiftKey&&e.keyCode==50){
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML("&nbsp;<span id='at"+at_index+"' class='at_span'>@</span>&nbsp;");
}else{
document.execCommand("insertHtml", false,"&nbsp;<span id='at"+at_index+"' class='at_span'>@</span>&nbsp;");
                }
e.preventDefault();
            }
        };

上述代码需要在当前光标位置插入内容,因此使用了range对象。

接下来是菜单的显示,关键在于如何准确定位。本文采用的方法较为直接:为插入的span元素添加id属性,然后根据该span的id位置来定位菜单。如果有更优的解决方案,欢迎交流。

具体实现逻辑如下

相关代码如下

function at_box_show(at){
var at_pos=avalon($(at)).position();
$('at_box').style.left=at_pos.left+'px';
$('at_box').style.top=at_pos.top+16+'px';
$('at_box').style.display='block';
    }
var at_index=0,cur_index=0;
avalon.define('editor', function(vm) {
vm.item_click=function(){
$('at'+cur_index).innerHTML="@"+this.innerHTML;
$('at_box').style.display='none';
at_index++;
        };
vm.check_key=function(e){
var editor=$('editor'),a=getCharacterPrecedingCaret(editor),range;
if(e.shiftKey&&e.keyCode==50){
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML("&nbsp;<span id='at"+at_index+"' class='at_span'>@</span>&nbsp;");
}else{
document.execCommand("insertHtml", false,"&nbsp;<span id='at"+at_index+"' class='at_span'>@</span>&nbsp;");
                }
at_box_show('at'+at_index);
cur_index=at_index;
e.preventDefault();
            }
        };
    });

此处根据新插入的span id,为at_box元素进行定位,随后显示菜单。cur_index变量用于记录光标当前所在的span id。设置该变量的原因是用户可能会回退修改已插入的span,而at_index是持续递增的,因此需要额外变量来追踪当前操作对象。

当用户点击菜单中的好友选项时,触发item_click回调函数。该回调将好友名字通过innerHTML属性添加到当前的span元素中,随后隐藏菜单,并使at_index自增。

Backspace删除与光标定位处理

上述逻辑监听了Shift+@组合键,接下来需要监听Backspace删除键。

相关代码如下

function getTextBeforeCursor(containerEl) {
var precedingChar = "", sel, range, precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
precedingChar = range.cloneContents();
            }
} else if ( (sel = document.selection)) {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
precedingChar = precedingRange.htmlText;
        }
return precedingChar;
    }

getTextBeforeCursor函数的作用是获取光标前的内容。由于浏览器兼容性差异,该函数在标准浏览器中返回的是光标前所有内容的DocumentFragment,而在IE中仅能获取文本(非node对象)。不过,这个html字符串可以转换为DocumentFragment。在avalon框架中,可使用parseHTML将html字符串转换为node对象;在jQuery中,通过$(html)[0]也能获取node对象。

利用该函数,再通过lastChild属性即可判断光标是否位于前文html的lastChild中,且该lastChild是否为span元素。

具体实现逻辑如下

相关代码如下

var a=getTextBeforeCursor($('editor'));
if(e.keyCode==8){
if(!-[1,]){
var b=avalon.parseHTML(a).lastChild;
}else{
var b=a.lastChild;
                }
if(b.nodeType==1&&b.nodeName=='SPAN'){
var id=b.id;
cur_index=b.id.substring(2);
at_box_show(b.id);
}else
$('at_box').style.display='none';
            }

鼠标悬停显示菜单逻辑

最后是光标进入span标签时显示菜单的功能。这显然需要绑定鼠标事件。这里选择绑定mouseup事件,因为如果绑定mousedown事件,用户需要在span标签上再次点击才能显示菜单。其原理与前述逻辑类似。

相关代码如下

vm.check_mouse=function(e){
var editor=$('editor'),a=getTextBeforeCursor(editor);
if(!-[1,]){
var b=avalon.parseHTML(getTextBeforeCursor(editor)).lastChild;
}else{
var b=a.lastChild;
            }
if(b!=null&&b.nodeType==1&&b.nodeName=='SPAN'){
var id=b.id;
cur_index=b.id.substring(2);
at_box_show(b.id);
}else
$('at_box').style.display='none';
        };

需要注意的是,如果光标位于span内部,则需要提取其id,at_box根据该id进行定位,同时重置cur_index变量。

至于通过ajax更新菜单和字符匹配的功能,此处不再赘述。

效果展示

Firefox浏览器下的效果

功能需求与兼容性要求

IE8浏览器下的效果

功能需求与兼容性要求

IE7浏览器下的效果

功能需求与兼容性要求

IE6浏览器下的效果

功能需求与兼容性要求

下载完整代码

以上就是本文所述的全部内容,希望对读者了解JavaScript有所帮助。

作者最新文章