• JS实现动态提示框


    引言

    什么项目都有个需求,应开发需求,需要写一个公式编辑器选择公式的插件,下面给大家讲一下实现过程。(擦汗,强作淡定,咳,开嗓~)

    看图说话

    本小菜开发功能前乐于先写个需求思维导图(纯属个人爱好):

    Html 

    真的只有一点点~

    复制代码
    <!DOCTYPE html>  
    <html>  
    <head>  
        <meta charset="UTF-8">
        <title>公式编辑器测试</title>
        <link rel="stylesheet" type="text/css" href="./css/global.css">
        <script type="text/javascript" src="./js/jquery-1.8.3.min.js"></script>  
        <script type="text/javascript" src="./js/editTips.js"></script> 
        
    </head>  
    <body> 
    
        <textarea id="test" ></textarea>
    
    </body>  
    </html>
    复制代码

    css

    一起给了吧~迟早要给:

    复制代码
    /* 编辑器下拉框相关样式 */
    table,tr,th,td{padding:0;margin:0;}
    ul,li,textarea,input{text-decoration:none;list-style:none;margin:0;padding:0;box-sizing: border-box;}
    input{ 
        outline:none;
     }
    .editTips{
        padding:5px 0;
        border-radius: 0!important;
        box-shadow: 0 2px 4px rgba(0,0,0,.2);
        max-height: auto;
        margin:0;
        z-index:9999;
    }
    .editTips li{
        text-align:left;
        box-sizing:border-box;
        display:block;
        width:100%;
        line-height:1.42857143;
        margin:1px 0;
        padding:6px 11px;
        color:#333;
        cursor:pointer;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        font-weight:400;
        line-height:1.42857143;
        border-bottom:solid 1px #e5e5e5;
        background:#e5e5e5;
    }
    .editTips li:last-child{
        border-bottom:none;
    }
    .active{ 
        background:#fee188!important; 
    } 
    .editTips li.active{ background:#fee188!important; }
    textarea{
        text-decoration: none;
        list-style: none;
        margin: 0;
        padding: 0;
        display: block;
        box-sizing: border-box;
        width: 500px;
        height: 300px;
        margin-top: 100px;
        margin-left: 100px;
    }
    复制代码

     插件模板 - this 为输入框

    复制代码
    (function ($) {
        $.fn.extend({
            "editTips": function (options) {
                var opts = $.extend({}, defaults, options); //使用jQuery.extend 覆盖插件默认参数
                return    this.each(function () { //这里的this 就是 jQuery对象
    
                    });
                }
            });
              //默认参数
              var defaults = {
    
              };                    
    })(window.jQuery);
    复制代码

    插入下拉框

    这里 this 为输入框,在 html 中,不需要再去js插入,当然这不是问题,在这里我们将 输入框和下拉框都自定义一下,插入的 ul 中 editTips 类可以去掉,为了方便看效果暂时加上

    我个人肯定是希望下拉框宽度是可以自定义的: 'width':opts.dropdownWidth, 这里我们定义了第一个参数: 下拉框宽度 

    复制代码
     1                     var _this = this;                      
     2                       _this.entertext = $(this);
     3                       _this.dropdown = $('<ul class="editTips" style="display:none;"></ul>');
     5                       $("body").after(_this.dropdown);                      
     6                     _this.dropdown.css({
     7                         'width':opts.dropdownWidth,
     8                         'position':'absolute',
     9                     });
    
    复制代码

     监听文本框

    ps:既然是公式编辑器,那说明不会一直触发检索事件,只有在输入了指定字符的时候才会触发检索事件,比如:! @ # 之类,这里我们以 $ 为例,从这里我们可以想到,我们肯定

    需要一个参数来动态改变检索事件触发条件,先记在心里

    检索流程: 输入框输入值  -  输入特定字符触发检索,如 $  -  是否符合放松请求条件:输入间隔一秒  -  调用回调函数,我们这里为 callbacktips 匹配关键字  -  返回包含关键字部分匹配结果,以数组形式  -  遍历数组添加到下拉框 ul 中显示  -  选取当前项 

    其中数组相关方法运用得较多

     因为其中还涉及到限制请求,方向键切换当前项,这里就不分开说了,直接给出整块代码如下:

    复制代码
    // 监听输入框
    _this.dropdown.parent().on("keyup",this,function(event){                        
        var nowTime =  window.sessionStorage.getItem('nowTime');
        // 当前项索引
        var n = _this.dropdown.find(".active").index();
        // li 个数
        var n_max = _this.dropdown.find("li").length;
        // 注意 event在 firefox 中不能兼容,在方法中带上event参数,如下声明实现兼容
        EVT = event || window.event;
        if( EVT.keyCode == 38 ){
            // 方向键控制 li 选项
            if(n-1>=0){
                _this.dropdown.find('li').eq(n-1).addClass("active").siblings().removeClass("active");
            }
            if( n == 0){
                _this.dropdown.find('li').eq(n_max-1).addClass("active").siblings().removeClass("active");
            }
            return false;
        }
         // 禁止enter键换行
        if( EVT.keyCode == 13 ){
            return false;
        }
        if( EVT.keyCode == 40 ){
            // 方向键控制 li 选项
            if(n<n_max-1){
                _this.dropdown.find('li').eq(n+1).addClass("active").siblings().removeClass("active");    
            }
            if( n+1 == n_max ){
                _this.dropdown.find('li').eq(0).addClass("active").siblings().removeClass("active");
            }
            return false;
        }
        // 限制请求,输入间隔不超过一秒时不触发检索事件发送请求
        if( nowTime){
            var oldTime = Date.now();
            var nowTime = window.sessionStorage.getItem('nowTime');
            var m =  parseInt((oldTime - nowTime)/1000);
            if( m >= 1){                                
                // 文本内容
                var val = _this.entertext.val();
                // 以空格切割文本返回数组
                var arr_test = val.split(" ");
                // 获取数组最后一个元素
                var temp = arr_test[arr_test.length-1];
                // 切割最后一个元素 单字符元素 返回数组
                var temp_type = temp.split("");
                // 获取数字第一个字符
                var temp_cha = temp_type[0];
                // 最后一个元素长度
                var temp_len = temp.length;
    
                var temp_dot = temp_type.lastIndexOf(".");
                // 定义回调函数 callbacktips
                var  callbacktips = function(arr_json){                            
                     // 初始化 UL 
                    _this.dropdown.find("li").remove();
                    for( i=0;i<arr_json.length;i++ ){
                        _this.dropdown.append('<li>'+arr_json[i]+'</li>');            
                    };
                    _this.dropdown.show();
                    _this.dropdown.find("li:first-child").addClass("active");
                    // 自定义样式
                    _this.dropdown.find("li").css({    
                        'width':'100%',
                    });
                };
                // 最后一个元素为空值 
                if( temp_len == 0 ){
                    _this.dropdown.hide();
                    _this.dropdown.find('li').remove();
                }
                // 为特定字符 符合触发条件 
                if( temp_cha == opts.triggerCharacter ){                            
                    if($.isFunction(opts.keyPressAction)){
                        
                        opts.keyPressAction(temp, function(arr_json){
                            // 调用回调函数
                            callbacktips(arr_json);
                        
                        });
                    }
                }else{
                    _this.dropdown.hide();
                    _this.dropdown.find('li').remove();
                }                 
            }
        }
        // 初始化第一次时间
        window.sessionStorage.setItem('nowTime',Date.now());
    });
    复制代码
    ps:这里出现了我们第二个参数,也是至关重要的一个参数,触发检索事件字符: opts.triggerCharacter

    鼠标切换当前项

    下拉菜单显示之后(这里我们先不管下拉菜单显示的位置),从体验上来说我希望既可以用鼠标选取,也可以用方向键选取,方向键切换当前项整合在上面的代码中,这里鼠标切换当前项:

    // 切换当前项
    _this.dropdown.on('mouseenter','li',function(){
        $(this).addClass("active").siblings().removeClass("active");
    });

    阻止键盘按键默认事件 - enter键选取当前项

    如果不做处理,我们在用方向键切换当前项的时候,你会看到光标也在上下移动,这如何能忍?怎么办?屏蔽它~,在这里我们可以整合enter键选取当前项,看代码:

    复制代码
    // 阻止输入过程中 方向键盘的默认事件
    _this.entertext.on("keydown",_this.entertext,function(event){
        EVT = event || window.event;
        if( EVT.keyCode == 38 ){
            EVT.preventDefault();
        }
        if( EVT.keyCode == 40 ){
            EVT.preventDefault();
        }
        // enter键选取对应的 li
        if( EVT.keyCode == 13 ){
            EVT.preventDefault();
            var txt = _this.dropdown.find(".active").html();
            var test_txt = _this.entertext.val();
            var arr_change = test_txt.split(" ");
            // 以空格切割文本返回数组
            var arr_test = test_txt.split(" ");
            // 获取数组最后一个元素
            var temp = arr_test[arr_test.length-1];
            var temp_type = temp.split("");
            var temp_dot = temp_type.lastIndexOf(".");
            var n ;
            temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
            n = temp_type.join('');
            arr_change.splice(arr_change.length-1,1,""+n);
            _this.entertext.val(arr_change.join(" "));
            _this.dropdown.hide();
            _this.dropdown.find('li').remove();
            }
    });
    复制代码

    点击当前项 重组val

    要不我们直接看代码吧,你可能已经饿了~好的。看代码:

    复制代码
    // 点击替换 重组val
    $(document).on("click",'li',function(){
        var txt = $(this).html();
        var test_txt = _this.entertext.val();
        var arr_change = test_txt.split(" ");
        // 以空格切割文本返回数组
        var arr_test = test_txt.split(" ");
        // 获取数组最后一个元素
        var temp = arr_test[arr_test.length-1];y
        var temp_type = temp.split("");
        var temp_dot = temp_type.lastIndexOf(".");
        var n ;
        temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
        n = temp_type.join('');
        arr_change.splice(arr_change.length-1,1,""+n);
        _this.entertext.val(arr_change.join(" "));
        _this.dropdown.hide();
        _this.dropdown.find('li').remove();
    });
    复制代码

    默认参数

    到了这里抛开动态显示下拉框位置,我们的公式编辑器应该可以跑一跑了,这里我们给参数设置一下默认值:

    //默认参数
    var defaults = {
        triggerCharacter : '$',
        dropdownWidth:'150px'
    };    

    这里我们将特定字符设定为 $,下拉框默认默认宽度为150px,当然其中还有一些配角参数,不屑一提,看官们有兴趣也可以自行添加一些参数

    测试之后发现是可以正常使用的,那么接下来就是下拉框显示位置的问题了,这个想象好像不是秒秒钟能解决的问题,怎么办?(吐槽:什么怎么办?问百度啊~不要问我搜不到怎么办,因为我也不知道~)

    这种时候不得不安慰自己:身为菜鸟还是有好处的,毕竟有无数前辈在前进的道路上闪闪发光~

    搜~

    掌声,在这里附上获取光标像素左边的原文链接以示感谢:http://blog.csdn.net/kingwolfofsky/article/details/6586029

    剩下的就是整合啦,当然对于整合过程中遇到的一点小问题不必讶异(都坐下,基本操作):

    1,测试之后发现下拉框位置是动态出现了,可位置不对,偏移心里的理想位置有八百里。没办法,再去安静的阅读一下前辈的代码,看完代码,发现上文中 下拉框的位置是绝对定位的坐标,这意味着什么

    呢?(我承认是后来才发现的~不测试不知道),这意味着下拉框的显示位置不是相对于输入框,而是相对于整个body,所以我们应该把下拉框插入在body里面:

    $("body").after(_this.dropdown);

    2,再测:发现位置大体上是对了,但还是有偏移,再测测测~原来下拉框显示位置还受输入框 margin-left 和 margin-top 的影响,测试得出以下代码:

    复制代码
    // 调用 kingwolfofsky, 获取光标坐标
    function show(elem) {  
        var p = kingwolfofsky.getInputPositon(elem);  
        var s = _this.dropdown.get(0); 
        var ttop = parseInt(_this.entertext.css("marginTop"));
        var tleft = parseInt(_this.entertext.css("marginLeft")) 
        console.log(ttop); 
        s.style.top = p.bottom-ttop+10+'px';  
        s.style.left = p.left-tleft + 'px';                    
    } 
    复制代码

    现在我们需要一次完整的测试,调用:

    复制代码
    $("#test").editTips({
        triggerCharacter : '$',
        dropdownWidth:'150px',  
        keyPressAction:function(temp,callbacktips){
            var arr_json;
            if( temp == "$" ){
                arr_json = ["$a","$ab","$b","$bb"]
            }
            if(temp && temp.indexOf("$a")== 0){
                arr_json = ["$a","$ab"];
            }
            else if(temp && temp.indexOf("$b")== 0){
                arr_json = ["$b","$bb"];
            }
            callbacktips(arr_json);
        }           
    });
    复制代码

    当然我们这里只是模拟返回数组,如果公式库不是很大,可以在前端完成,比如说自己建一个 json文件啥的~测试结果如图:

    全部 js 代码

    效果很ok,那么接下来呢?看官息怒,我知道到了该出全部代码的时候到了:

    复制代码
    /*
    *****公式编辑器*****
    * 调用 editTips()方法
    * editTips({
    *        triggerCharacter:  触发匹配字符 默认为 "$"
    *        textareaWidth:        输入框宽度 默认 auto
    *        textareaHeight:    输入框高度 默认 auto
    *        dropdownWidth:        下拉提示框宽度 默认150px
    *        keyPressAction:function(temp,callbacktips){  
    *        // 参数为temp 返回 arr_json 数组 调用回调函数 callbacktips(arr_json)
    *            var arr_json;
    *            callbacktips(arr_json);
    *        }            
    *   });
    * 
    */
    
    (function ($) {
        $.fn.extend({
            "editTips": function (options) {
                var opts = $.extend({}, defaults, options); //使用jQuery.extend 覆盖插件默认参数
                return    this.each(function () { //这里的this 就是 jQuery对象
                        // 获取输入光标在页面中的坐标 返回left和top,bottom  
                        var kingwolfofsky = {  
                            getInputPositon: function (elem) {  
                                if (document.selection) {   //IE Support  
                                    elem.focus();  
                                    var Sel = document.selection.createRange();  
                                    return {  
                                        left: Sel.boundingLeft,  
                                        top: Sel.boundingTop,  
                                        bottom: Sel.boundingTop + Sel.boundingHeight  
                                    };  
                                } else {  
                                    var that = this;  
                                    var cloneDiv = '{$clone_div}', cloneLeft = '{$cloneLeft}', cloneFocus = '{$cloneFocus}', cloneRight = '{$cloneRight}';  
                                    var none = '<span style="white-space:pre-wrap;"> </span>';  
                                    var div = elem[cloneDiv] || document.createElement('div'), focus = elem[cloneFocus] || document.createElement('span');  
                                    var text = elem[cloneLeft] || document.createElement('span');  
                                    var offset = that._offset(elem), index = this._getFocus(elem), focusOffset = { left: 0, top: 0 };  
                          
                                    if (!elem[cloneDiv]) {  
                                        elem[cloneDiv] = div, elem[cloneFocus] = focus;  
                                        elem[cloneLeft] = text;  
                                        div.appendChild(text);  
                                        div.appendChild(focus);  
                                        document.body.appendChild(div);  
                                        focus.innerHTML = '|';  
                                        focus.style.cssText = 'display:inline-block;0px;overflow:hidden;z-index:-100;word-wrap:break-word;word-break:break-all;';  
                                        div.className = this._cloneStyle(elem);  
                                        div.style.cssText = 'visibility:hidden;display:inline-block;position:absolute;z-index:-100;word-wrap:break-word;word-break:break-all;overflow:hidden;';  
                                    };  
                                    div.style.left = this._offset(elem).left + "px";  
                                    div.style.top = this._offset(elem).top + "px";  
                                    var strTmp = elem.value.substring(0, index).replace(/</g, '<').replace(/>/g, '>').replace(/
    /g, '<br/>').replace(/s/g, none);  
                                    text.innerHTML = strTmp;  
                          
                                    focus.style.display = 'inline-block';  
                                    try { focusOffset = this._offset(focus); } catch (e) { };  
                                    focus.style.display = 'none';  
                                    return {  
                                        left: focusOffset.left,  
                                        top: focusOffset.top,  
                                        bottom: focusOffset.bottom  
                                    };  
                                }  
                            },
                                // 克隆元素样式并返回类  
                            _cloneStyle: function (elem, cache) {  
                                if (!cache && elem['${cloneName}']) return elem['${cloneName}'];  
                                var className, name, rstyle = /^(number|string)$/;  
                                var rname = /^(content|outline|outlineWidth)$/; //Opera: content; IE8:outline && outlineWidth  
                                var cssText = [], sStyle = elem.style;  
                          
                                for (name in sStyle) {  
                                    if (!rname.test(name)) {  
                                        val = this._getStyle(elem, name);  
                                        if (val !== '' && rstyle.test(typeof val)) { // Firefox 4  
                                            name = name.replace(/([A-Z])/g, "-$1").toLowerCase();  
                                            cssText.push(name);  
                                            cssText.push(':');  
                                            cssText.push(val);  
                                            cssText.push(';');  
                                        };  
                                    };  
                                };  
                                cssText = cssText.join('');  
                                elem['${cloneName}'] = className = 'clone' + (new Date).getTime();  
                                this._addHeadStyle('.' + className + '{' + cssText + '}');  
                                return className;  
                            },  
                          
                            // 向页头插入样式  
                            _addHeadStyle: function (content) {  
                                var style = this._style[document];  
                                if (!style) {  
                                    style = this._style[document] = document.createElement('style');  
                                    document.getElementsByTagName('head')[0].appendChild(style);  
                                };  
                                style.styleSheet && (style.styleSheet.cssText += content) || style.appendChild(document.createTextNode(content));  
                            },  
                            _style: {},  
                          
                            // 获取最终样式  
                            _getStyle: 'getComputedStyle' in window ? function (elem, name) {  
                                return getComputedStyle(elem, null)[name];  
                            } : function (elem, name) {  
                                return elem.currentStyle[name];  
                            },  
                                // 获取光标在文本框的位置  
                            _getFocus: function (elem) {  
                                var index = 0;  
                                if (document.selection) {// IE Support  
                                    elem.focus();  
                                    var Sel = document.selection.createRange();  
                                    if (elem.nodeName === 'TEXTAREA') {//textarea  
                                        var Sel2 = Sel.duplicate();  
                                        Sel2.moveToElementText(elem);  
                                        var index = -1;  
                                        while (Sel2.inRange(Sel)) {  
                                            Sel2.moveStart('character');  
                                            index++;  
                                        };  
                                    }  
                                    else if (elem.nodeName === 'INPUT') {// input  
                                        Sel.moveStart('character', -elem.value.length);  
                                        index = Sel.text.length;  
                                    }  
                                }  
                                else if (elem.selectionStart || elem.selectionStart == '0') { // Firefox support  
                                    index = elem.selectionStart;  
                                }  
                                return (index);  
                            },  
                          
                            // 获取元素在页面中位置  
                            _offset: function (elem) {  
                                var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement;  
                                var clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0;  
                                var top = box.top + (self.pageYOffset || docElem.scrollTop) - clientTop, left = box.left + (self.pageXOffset || docElem.scrollLeft) - clientLeft;  
                                return {  
                                    left: left,  
                                    top: top,  
                                    right: left + box.width,  
                                    bottom: top + box.height  
                                };  
                            }  
                        };  
                        // 文本框监听事件 
                        var _this = this;                      
                          _this.entertext = $(this);
                          _this.dropdown = $('<ul class="editTips" style="display:none;"></ul>');
                          // 获取到的弹出的下拉框的位置是绝对定位的坐标,所以得把弹出的层放到body里
                          $("body").after(_this.dropdown);                      
                        _this.dropdown.css({
                            'width':opts.dropdownWidth,
                            'position':'absolute',
                        });
                        $(this).css({
                              'position': 'relative',
    
                          });
                          
                        // 监听输入框
                        _this.dropdown.parent().on("keyup",this,function(event){                        
                            var nowTime =  window.sessionStorage.getItem('nowTime');
                            // 当前项索引
                            var n = _this.dropdown.find(".active").index();
                            // li 个数
                            var n_max = _this.dropdown.find("li").length;
                            // 注意 event在 firefox 中不能兼容,在方法中带上event参数,如下声明实现兼容
                            EVT = event || window.event;
                            if( EVT.keyCode == 38 ){
                                // 方向键控制 li 选项
                                if(n-1>=0){
                                    _this.dropdown.find('li').eq(n-1).addClass("active").siblings().removeClass("active");
                                }
                                if( n == 0){
                                    _this.dropdown.find('li').eq(n_max-1).addClass("active").siblings().removeClass("active");
                                }
                                return false;
                            }
                             // 禁止enter键换行
                            if( EVT.keyCode == 13 ){
                                return false;
                            }
                            if( EVT.keyCode == 40 ){
                                // 方向键控制 li 选项
                                if(n<n_max-1){
                                    _this.dropdown.find('li').eq(n+1).addClass("active").siblings().removeClass("active");    
                                }
                                if( n+1 == n_max ){
                                    _this.dropdown.find('li').eq(0).addClass("active").siblings().removeClass("active");
                                }
                                return false;
                            }
                            // 限制请求,输入间隔不超过一秒时不触发检索事件发送请求
                            if( nowTime){
                                var oldTime = Date.now();
                                var nowTime = window.sessionStorage.getItem('nowTime');
                                var m =  parseInt((oldTime - nowTime)/1000);
                                if( m >= 1){                                
                                    // 文本内容
                                    var val = _this.entertext.val();
                                    // 以空格切割文本返回数组
                                    var arr_test = val.split(" ");
                                    // 获取数组最后一个元素
                                    var temp = arr_test[arr_test.length-1];
                                    // 切割最后一个元素 单字符元素 返回数组
                                    var temp_type = temp.split("");
                                    // 获取数字第一个字符
                                    var temp_cha = temp_type[0];
                                    // 最后一个元素长度
                                    var temp_len = temp.length;
    
                                    var temp_dot = temp_type.lastIndexOf(".");
                                    // 定义回调函数 callbacktips
                                    var  callbacktips = function(arr_json){                            
                                         // 初始化 UL 
                                        _this.dropdown.find("li").remove();
                                        for( i=0;i<arr_json.length;i++ ){
                                            _this.dropdown.append('<li>'+arr_json[i]+'</li>');            
                                        };
                                        _this.dropdown.show();
                                        _this.dropdown.find("li:first-child").addClass("active");
                                        // 自定义样式
                                        _this.dropdown.find("li").css({    
                                            'width':'100%',
                                        });
                                    };
                                    // 最后一个元素为空值 
                                    if( temp_len == 0 ){
                                        _this.dropdown.hide();
                                        _this.dropdown.find('li').remove();
                                    }
                                    // 为特定字符 符合触发条件 
                                    if( temp_cha == opts.triggerCharacter ){                            
                                        if($.isFunction(opts.keyPressAction)){
                                            
                                            opts.keyPressAction(temp, function(arr_json){
                                                // 调用回调函数
                                                callbacktips(arr_json);
                                            
                                            });
                                        }
                                    }else{
                                        _this.dropdown.hide();
                                        _this.dropdown.find('li').remove();
                                    }                 
                                }
                            }
                            // 初始化第一次时间
                            window.sessionStorage.setItem('nowTime',Date.now());
                        });
    
                        // 切换当前项
                        _this.dropdown.on('mouseenter','li',function(){
                            $(this).addClass("active").siblings().removeClass("active");
                        });
                        // 阻止输入过程中 方向键盘的默认事件
                        _this.entertext.on("keydown",_this.entertext,function(event){
                            EVT = event || window.event;
                            if( EVT.keyCode == 38 ){
                                EVT.preventDefault();
                            }
                            if( EVT.keyCode == 40 ){
                                EVT.preventDefault();
                            }
                            // enter键选取对应的 li
                            if( EVT.keyCode == 13 ){
                                EVT.preventDefault();
                                var txt = _this.dropdown.find(".active").html();
                                var test_txt = _this.entertext.val();
                                var arr_change = test_txt.split(" ");
                                // 以空格切割文本返回数组
                                var arr_test = test_txt.split(" ");
                                // 获取数组最后一个元素
                                var temp = arr_test[arr_test.length-1];
                                var temp_type = temp.split("");
                                var temp_dot = temp_type.lastIndexOf(".");
                                var n ;
                                temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
                                n = temp_type.join('');
                                arr_change.splice(arr_change.length-1,1,""+n);
                                _this.entertext.val(arr_change.join(" "));
                                _this.dropdown.hide();
                                _this.dropdown.find('li').remove();
                                }
                        });
                        // 点击替换 重组val
                        $(document).on("click",'li',function(){
                            var txt = $(this).html();
                            var test_txt = _this.entertext.val();
                            var arr_change = test_txt.split(" ");
                            // 以空格切割文本返回数组
                            var arr_test = test_txt.split(" ");
                            // 获取数组最后一个元素
                            var temp = arr_test[arr_test.length-1];
                            var temp_type = temp.split("");
                            var temp_dot = temp_type.lastIndexOf(".");
                            var n ;
                            temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
                            n = temp_type.join('');
                            arr_change.splice(arr_change.length-1,1,""+n);
                            _this.entertext.val(arr_change.join(" "));
                            _this.dropdown.hide();
                            _this.dropdown.find('li').remove();
                        });
                        // 调用获取坐标方法 show(elem)
                        $(this).keyup(function(){
                            show(this);
                        });                
                        // 调用 kingwolfofsky, 获取光标坐标
                        function show(elem) {  
                            var p = kingwolfofsky.getInputPositon(elem);  
                            var s = _this.dropdown.get(0); 
                            var ttop = parseInt(_this.entertext.css("marginTop"));
                            var tleft = parseInt(_this.entertext.css("marginLeft")) 
                            console.log(ttop); 
                            s.style.top = p.bottom-ttop+10+'px';  
                            s.style.left = p.left-tleft + 'px';                    
                        } 
                    });
                }
            });
                //默认参数
    var defaults = {
        triggerCharacter : '$',
        dropdownWidth:'150px'
    };                    
    })(window.jQuery);
    复制代码

    调用插件

    对呀,还是上面的调用,没问题的吧~

    复制代码
            $("#test").editTips({
                triggerCharacter : '$',
                dropdownWidth:'150px',  
                keyPressAction:function(temp,callbacktips){
                    var arr_json;
                    if( temp == "$" ){
                        arr_json = ["$a","$ab","$b","$bb"]
                    }
                    if(temp && temp.indexOf("$a")== 0){
                        arr_json = ["$a","$ab"];
                    }
                    else if(temp && temp.indexOf("$b")== 0){
                        arr_json = ["$b","$bb"];
                    }
                    callbacktips(arr_json);
                }           
            });
    复制代码

    最后,复制粘贴上面的 html ,css ,js ,调用,它就是你的了~

    这次分享就到这里了,欢迎亲们品鉴,有问题可以私信或者留言哦~(偷笑:虽然我不一定看~看了不一定回~回了不一定能解决问题~)

     完整代码:

    <!DOCTYPE html>  
    <html>  
    <head>  
        <meta charset="UTF-8">
        <title>公式测试</title>
    
    
    <style>
    
    
    /* 编辑器下拉框相关样式 */
    table,tr,th,td{padding:0;margin:0;}
    ul,li,textarea,input{text-decoration:none;list-style:none;margin:0;padding:0;box-sizing: border-box;}
    input{ 
        outline:none;
     }
    .editTips{
        padding:5px 0;
        border-radius: 0!important;
        box-shadow: 0 2px 4px rgba(0,0,0,.2);
        max-height: auto;
        margin:0;
        z-index:9999;
    }
    .editTips li{
        text-align:left;
        box-sizing:border-box;
        display:block;
        100%;
        line-height:1.42857143;
        margin:1px 0;
        padding:6px 11px;
        color:#333;
        cursor:pointer;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        font-weight:400;
        line-height:1.42857143;
        border-bottom:solid 1px #e5e5e5;
        background:#e5e5e5;
    }
    .editTips li:last-child{
        border-bottom:none;
    }
    .active{ 
        background:#fee188!important; 
    } 
    .editTips li.active{ background:#fee188!important; }
    textarea{
        text-decoration: none;
        list-style: none;
        margin: 0;
        padding: 0;
        display: block;
        box-sizing: border-box;
         500px;
        height: 300px;
        margin-top: 100px;
        margin-left: 100px;
    }
    
    
    
    </style>    
    
    
    
    </head>  
    <body> 
        <textarea id="test" ></textarea>
    
    </body>  
    </html>
    
    
    <script type="text/javascript" src="./jquery-1.8.3.min.js"></script>  
    
    <script type="text/javascript">
    
    /*
    *****公式编辑器*****
    * 调用 editTips()方法
    * editTips({
    *        triggerCharacter:  触发匹配字符 默认为 "$"
    *        textareaWidth:        输入框宽度 默认 auto
    *        textareaHeight:    输入框高度 默认 auto
    *        dropdownWidth:        下拉提示框宽度 默认150px
    *        keyPressAction:function(temp,callbacktips){  
    *        // 参数为temp 返回 arr_json 数组 调用回调函数 callbacktips(arr_json)
    *            var arr_json;
    *            callbacktips(arr_json);
    *        }            
    *   });
    * 
    */
    
    (function ($) {
        $.fn.extend({
            "editTips": function (options) {
                var opts = $.extend({}, defaults, options); //使用jQuery.extend 覆盖插件默认参数
                return    this.each(function () { //这里的this 就是 jQuery对象
                        // 获取输入光标在页面中的坐标 返回left和top,bottom  
                        var kingwolfofsky = {  
                            getInputPositon: function (elem) {  
                                if (document.selection) {   //IE Support  
                                    elem.focus();  
                                    var Sel = document.selection.createRange();  
                                    return {  
                                        left: Sel.boundingLeft,  
                                        top: Sel.boundingTop,  
                                        bottom: Sel.boundingTop + Sel.boundingHeight  
                                    };  
                                } else {  
                                    var that = this;  
                                    var cloneDiv = '{$clone_div}', cloneLeft = '{$cloneLeft}', cloneFocus = '{$cloneFocus}', cloneRight = '{$cloneRight}';  
                                    var none = '<span style="white-space:pre-wrap;"> </span>';  
                                    var div = elem[cloneDiv] || document.createElement('div'), focus = elem[cloneFocus] || document.createElement('span');  
                                    var text = elem[cloneLeft] || document.createElement('span');  
                                    var offset = that._offset(elem), index = this._getFocus(elem), focusOffset = { left: 0, top: 0 };  
                          
                                    if (!elem[cloneDiv]) {  
                                        elem[cloneDiv] = div, elem[cloneFocus] = focus;  
                                        elem[cloneLeft] = text;  
                                        div.appendChild(text);  
                                        div.appendChild(focus);  
                                        document.body.appendChild(div);  
                                        focus.innerHTML = '|';  
                                        focus.style.cssText = 'display:inline-block;0px;overflow:hidden;z-index:-100;word-wrap:break-word;word-break:break-all;';  
                                        div.className = this._cloneStyle(elem);  
                                        div.style.cssText = 'visibility:hidden;display:inline-block;position:absolute;z-index:-100;word-wrap:break-word;word-break:break-all;overflow:hidden;';  
                                    };  
                                    div.style.left = this._offset(elem).left + "px";  
                                    div.style.top = this._offset(elem).top + "px";  
                                    var strTmp = elem.value.substring(0, index).replace(/</g, '<').replace(/>/g, '>').replace(/
    /g, '<br/>').replace(/s/g, none);  
                                    text.innerHTML = strTmp;  
                          
                                    focus.style.display = 'inline-block';  
                                    try { focusOffset = this._offset(focus); } catch (e) { };  
                                    focus.style.display = 'none';  
                                    return {  
                                        left: focusOffset.left,  
                                        top: focusOffset.top,  
                                        bottom: focusOffset.bottom  
                                    };  
                                }  
                            },
                                // 克隆元素样式并返回类  
                            _cloneStyle: function (elem, cache) {  
                                if (!cache && elem['${cloneName}']) return elem['${cloneName}'];  
                                var className, name, rstyle = /^(number|string)$/;  
                                var rname = /^(content|outline|outlineWidth)$/; //Opera: content; IE8:outline && outlineWidth  
                                var cssText = [], sStyle = elem.style;  
                          
                                for (name in sStyle) {  
                                    if (!rname.test(name)) {  
                                        val = this._getStyle(elem, name);  
                                        if (val !== '' && rstyle.test(typeof val)) { // Firefox 4  
                                            name = name.replace(/([A-Z])/g, "-$1").toLowerCase();  
                                            cssText.push(name);  
                                            cssText.push(':');  
                                            cssText.push(val);  
                                            cssText.push(';');  
                                        };  
                                    };  
                                };  
                                cssText = cssText.join('');  
                                elem['${cloneName}'] = className = 'clone' + (new Date).getTime();  
                                this._addHeadStyle('.' + className + '{' + cssText + '}');  
                                return className;  
                            },  
                          
                            // 向页头插入样式  
                            _addHeadStyle: function (content) {  
                                var style = this._style[document];  
                                if (!style) {  
                                    style = this._style[document] = document.createElement('style');  
                                    document.getElementsByTagName('head')[0].appendChild(style);  
                                };  
                                style.styleSheet && (style.styleSheet.cssText += content) || style.appendChild(document.createTextNode(content));  
                            },  
                            _style: {},  
                          
                            // 获取最终样式  
                            _getStyle: 'getComputedStyle' in window ? function (elem, name) {  
                                return getComputedStyle(elem, null)[name];  
                            } : function (elem, name) {  
                                return elem.currentStyle[name];  
                            },  
                                // 获取光标在文本框的位置  
                            _getFocus: function (elem) {  
                                var index = 0;  
                                if (document.selection) {// IE Support  
                                    elem.focus();  
                                    var Sel = document.selection.createRange();  
                                    if (elem.nodeName === 'TEXTAREA') {//textarea  
                                        var Sel2 = Sel.duplicate();  
                                        Sel2.moveToElementText(elem);  
                                        var index = -1;  
                                        while (Sel2.inRange(Sel)) {  
                                            Sel2.moveStart('character');  
                                            index++;  
                                        };  
                                    }  
                                    else if (elem.nodeName === 'INPUT') {// input  
                                        Sel.moveStart('character', -elem.value.length);  
                                        index = Sel.text.length;  
                                    }  
                                }  
                                else if (elem.selectionStart || elem.selectionStart == '0') { // Firefox support  
                                    index = elem.selectionStart;  
                                }  
                                return (index);  
                            },  
                          
                            // 获取元素在页面中位置  
                            _offset: function (elem) {  
                                var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement;  
                                var clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0;  
                                var top = box.top + (self.pageYOffset || docElem.scrollTop) - clientTop, left = box.left + (self.pageXOffset || docElem.scrollLeft) - clientLeft;  
                                return {  
                                    left: left,  
                                    top: top,  
                                    right: left + box.width,  
                                    bottom: top + box.height  
                                };  
                            }  
                        };  
                        // 文本框监听事件 
                        var _this = this;                      
                          _this.entertext = $(this);
                          _this.dropdown = $('<ul class="editTips" style="display:none;"></ul>');
                          // 获取到的弹出的下拉框的位置是绝对定位的坐标,所以得把弹出的层放到body里
                          $("body").after(_this.dropdown);                      
                        _this.dropdown.css({
                            'width':opts.dropdownWidth,
                            'position':'absolute',
                        });
                        $(this).css({
                              'position': 'relative',
    
                          });
                          
                        // 监听输入框
                        _this.dropdown.parent().on("keyup",this,function(event){                        
                            var nowTime =  window.sessionStorage.getItem('nowTime');
                            // 当前项索引
                            var n = _this.dropdown.find(".active").index();
                            // li 个数
                            var n_max = _this.dropdown.find("li").length;
                            // 注意 event在 firefox 中不能兼容,在方法中带上event参数,如下声明实现兼容
                            EVT = event || window.event;
                            if( EVT.keyCode == 38 ){
                                // 方向键控制 li 选项
                                if(n-1>=0){
                                    _this.dropdown.find('li').eq(n-1).addClass("active").siblings().removeClass("active");
                                }
                                if( n == 0){
                                    _this.dropdown.find('li').eq(n_max-1).addClass("active").siblings().removeClass("active");
                                }
                                return false;
                            }
                             // 禁止enter键换行
                            if( EVT.keyCode == 13 ){
                                return false;
                            }
                            if( EVT.keyCode == 40 ){
                                // 方向键控制 li 选项
                                if(n<n_max-1){
                                    _this.dropdown.find('li').eq(n+1).addClass("active").siblings().removeClass("active");    
                                }
                                if( n+1 == n_max ){
                                    _this.dropdown.find('li').eq(0).addClass("active").siblings().removeClass("active");
                                }
                                return false;
                            }
                            // 限制请求,输入间隔不超过一秒时不触发检索事件发送请求
                            if( nowTime){
                                var oldTime = Date.now();
                                var nowTime = window.sessionStorage.getItem('nowTime');
                                var m =  parseInt((oldTime - nowTime)/1000);
                                if( m >= 1){                                
                                    // 文本内容
                                    var val = _this.entertext.val();
                                    // 以空格切割文本返回数组
                                    var arr_test = val.split(" ");
                                    // 获取数组最后一个元素
                                    var temp = arr_test[arr_test.length-1];
                                    // 切割最后一个元素 单字符元素 返回数组
                                    var temp_type = temp.split("");
                                    // 获取数字第一个字符
                                    var temp_cha = temp_type[0];
                                    // 最后一个元素长度
                                    var temp_len = temp.length;
    
                                    var temp_dot = temp_type.lastIndexOf(".");
                                    // 定义回调函数 callbacktips
                                    var  callbacktips = function(arr_json){                            
                                         // 初始化 UL 
                                        _this.dropdown.find("li").remove();
                                        for( i=0;i<arr_json.length;i++ ){
                                            _this.dropdown.append('<li>'+arr_json[i]+'</li>');            
                                        };
                                        _this.dropdown.show();
                                        _this.dropdown.find("li:first-child").addClass("active");
                                        // 自定义样式
                                        _this.dropdown.find("li").css({    
                                            'width':'100%',
                                        });
                                    };
                                    // 最后一个元素为空值 
                                    if( temp_len == 0 ){
                                        _this.dropdown.hide();
                                        _this.dropdown.find('li').remove();
                                    }
                                    // 为特定字符 符合触发条件 
                                    if( temp_cha == opts.triggerCharacter ){                            
                                        if($.isFunction(opts.keyPressAction)){
                                            
                                            opts.keyPressAction(temp, function(arr_json){
                                                // 调用回调函数
                                                callbacktips(arr_json);
                                            
                                            });
                                        }
                                    }else{
                                        _this.dropdown.hide();
                                        _this.dropdown.find('li').remove();
                                    }                 
                                }
                            }
                            // 初始化第一次时间
                            window.sessionStorage.setItem('nowTime',Date.now());
                        });
    
                        // 切换当前项
                        _this.dropdown.on('mouseenter','li',function(){
                            $(this).addClass("active").siblings().removeClass("active");
                        });
                        // 阻止输入过程中 方向键盘的默认事件
                        _this.entertext.on("keydown",_this.entertext,function(event){
                            EVT = event || window.event;
                            if( EVT.keyCode == 38 ){
                                EVT.preventDefault();
                            }
                            if( EVT.keyCode == 40 ){
                                EVT.preventDefault();
                            }
                            // enter键选取对应的 li
                            if( EVT.keyCode == 13 ){
                                EVT.preventDefault();
                                var txt = _this.dropdown.find(".active").html();
                                var test_txt = _this.entertext.val();
                                var arr_change = test_txt.split(" ");
                                // 以空格切割文本返回数组
                                var arr_test = test_txt.split(" ");
                                // 获取数组最后一个元素
                                var temp = arr_test[arr_test.length-1];
                                var temp_type = temp.split("");
                                var temp_dot = temp_type.lastIndexOf(".");
                                var n ;
                                temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
                                n = temp_type.join('');
                                arr_change.splice(arr_change.length-1,1,""+n);
                                _this.entertext.val(arr_change.join(" "));
                                _this.dropdown.hide();
                                _this.dropdown.find('li').remove();
                                }
                        });
                        // 点击替换 重组val
                        $(document).on("click",'li',function(){
                            var txt = $(this).html();
                            var test_txt = _this.entertext.val();
                            var arr_change = test_txt.split(" ");
                            // 以空格切割文本返回数组
                            var arr_test = test_txt.split(" ");
                            // 获取数组最后一个元素
                            var temp = arr_test[arr_test.length-1];
                            var temp_type = temp.split("");
                            var temp_dot = temp_type.lastIndexOf(".");
                            var n ;
                            temp_type.splice(temp_dot+1,temp_type.length-1-temp_dot,txt);
                            n = temp_type.join('');
                            arr_change.splice(arr_change.length-1,1,""+n);
                            _this.entertext.val(arr_change.join(" "));
                            _this.dropdown.hide();
                            _this.dropdown.find('li').remove();
                        });
                        // 调用获取坐标方法 show(elem)
                        $(this).keyup(function(){
                            show(this);
                        });                
                        // 调用 kingwolfofsky, 获取光标坐标
                        function show(elem) {  
                            var p = kingwolfofsky.getInputPositon(elem);  
                            var s = _this.dropdown.get(0); 
                            var ttop = parseInt(_this.entertext.css("marginTop"));
                            var tleft = parseInt(_this.entertext.css("marginLeft")) 
                            console.log(ttop); 
                            s.style.top = p.bottom-ttop+10+'px';  
                            s.style.left = p.left-tleft + 'px';                    
                        } 
                    });
                }
            });
                //默认参数
    var defaults = {
        triggerCharacter : '$',
        dropdownWidth:'150px'
    };                    
    })(window.jQuery);
    
    
    
    
    //调用,还是上面的调用
           $("#test").editTips({
                triggerCharacter : '$',
                dropdownWidth:'150px',  
                keyPressAction:function(temp,callbacktips){
                    var arr_json;
                    if( temp == "$" ){
                        arr_json = ["$a","$ab","$b","$bb 副科级"]
                    }
                    if(temp && temp.indexOf("$a")== 0){
                        arr_json = ["$a","$ab"];
                    }
                    else if(temp && temp.indexOf("$b")== 0){
                        arr_json = ["$b","$bb  这个是说明"];
                    }
                    callbacktips(arr_json);
                }           
            });
    
    
    </script> 
    View Code

    如果想使用的时候显得高大上的感觉,其中的不足和高级功能需要你自己去补充了。

    出处:http://www.cnblogs.com/wbsndbf/p/7976300.html

  • 相关阅读:
    python播放音频文件
    安装pyaudio
    给 python工程 打包并上传 PyPI (The Python Package Index)
    python怎么import自己写的包
    pip源使用国内镜像
    Git和GitHub
    nginx的日志轮转
    ab接口压力测试工具
    nginx 性能优化
    https协议
  • 原文地址:https://www.cnblogs.com/mq0036/p/7986385.html
Copyright © 2020-2023  润新知