zoukankan      html  css  js  c++  java
  • js实现@提到好友

    要求

    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的位置为菜单定位,当然后台需要把span id去掉,因为假设当前页面有id='at0'的话,会影响定位,和插入@。如果有更好的做法,请告诉我一声。

    具体的

    复制代码
        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();
                }
            };
        });
    复制代码

    at_show_box根据新插入的span id,为at_box定位,然后显示菜单。cur_index表示光标当前所在的span id.设置这个变量因为用户可能倒回去改已经插入的span,而at_index是一直递增的,所以这里就还需要一个变量。

    用户点击菜单中好友项,触发item_click回调。回调里就是将好友名字用innserHTML添加到当前span里面.然后隐藏菜单,at_index++。

    上面是监听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.

    效果

    firefox

    ie8

    ie7

    ie6

    最后有个需要注意的地方,在上面的效果中,光标实际上是没有在span里面的。仔细看上面的话就会发现按下@,光标和@间有间距。也就是说,光标在span外,这会导致当输入时,传给后台的是span的innerHTML,但是这个innerHTML始终是没有值的。

    解决方法就是弹出菜单参数时,让光标跳到span里面。

    复制代码
        function po_Last_Div(obj) {
            if (window.getSelection) {
                obj.focus();
                var range = window.getSelection();
                range.selectAllChildren(obj);
                range.collapseToEnd();
            }
            else if (document.selection) {
                var range = document.selection.createRange();
                range.moveToElementText(obj);
                range.collapse(false);
                range.select();
            }
        }
    复制代码

    直接po_Last_Div($('at')+cur_index)就可以了

    好人做到底吧,附上取出好友的正则表达式 /<span[^>]*>([sS]*?)</span>/gi

    用法

    var at_list=[];
            content.replace(/<span[^>]*>([sS]*?)</span>/gi, function (match, user) {
                at_list.push(user.substring(1))//从@后开始取
            });
    

      

    转自: TheViper http://www.cnblogs.com/TheViper

  • 相关阅读:
    一行代码更改博客园皮肤
    fatal: refusing to merge unrelated histories
    使用 netcat 传输大文件
    linux 命令后台运行
    .net core 使用 Nlog 配置文件
    .net core 使用 Nlog 集成 exceptionless 配置文件
    Mysql不同字符串格式的连表查询
    Mongodb between 时间范围
    VS Code 使用 Debugger for Chrome 调试vue
    css权重说明
  • 原文地址:https://www.cnblogs.com/ygunoil/p/13503648.html
Copyright © 2011-2022 走看看