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

    转载请注明: TheViper http://www.cnblogs.com/TheViper

    要求

    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))//从@后开始取
            });

    例子下载

     

  • 相关阅读:
    小白学开发(iOS)OC_ 使用继承来扩充类(2015-08-07)
    UI组件之TextView及其子类(三)ToggleButton和Switch
    C++智能指针--shared_ptr
    HDU 1013 Digital Roots 题解
    对touch事件传递的简单理解
    【LeetCode-面试算法经典-Java实现】【096-Unique Binary Search Trees(唯一二叉搜索树)】
    Cocos2d-x 坐标系
    hdu1518 Square
    servlet3.0新特性
    OGNL表达式
  • 原文地址:https://www.cnblogs.com/TheViper/p/4339042.html
Copyright © 2011-2022 走看看