(function ($) { $.fn.extend({ "editTips": function (options) { var opts = $.extend({}, defaults, options); return this.each(function(e) { /*********以下为获取下拉框像素坐标方法*********/ var Teleprompter = { 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 = ' '; 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;width: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(/\n/g, '
').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 = me = $(this); var e = event || window.event; // 键值兼容 var searchStart = false; // 设置检索事件开关 var checkCharacter = false; // 输入字符检索开关 var oldCurrentPos = ''; // 检索值开始的位置 var currentPos = ''; // 检索值结束的位置 var selectVal = ''; // 检索值 var pos = ''; // 设置光标位置 var enterCharacter = ''; // 当前输入的字符 var dotVal; // 输入 . 时从0到当前光标位置文本 var dotDollerPos; // 获取往后查找离 . 最近的 $ 的下标,引文输入 . 时的检索值即dotSelectVal不包含 $ 本身,所以需要加1 var dotSelectVal; // 输入 . 时的检索值 // 插入下拉框 me.dropdown = $(''); $("body").after(me.dropdown); me.dropdown.css({ 'width': opts.dropdownWidth, 'position':'absolute', }); // 获取当前光标位置 currentPos var getStart = function() { var all_range = ''; if (navigator.userAgent.indexOf("MSIE") > -1) { //IE if( _this.get(0).tagName == "TEXTAREA" ){ // 根据body创建textRange all_range = document.body.createTextRange(); // 让textRange范围包含元素里所有内容 all_range.moveToElementText(_this.get(0)); } else { // 根据当前输入元素类型创建textRange all_range = _this.get(0).createTextRange(); } // 输入元素获取焦点 _this.focus(); // 获取当前的textRange,如果当前的textRange是一个具体位置而不是范围,textRange的范围从currentPos到end.此时currentPos等于end var cur_range = document.selection.createRange(); // 将当前的textRange的end向前移"选中的文本.length"个单位.保证currentPos=end cur_range.moveEnd('character',-cur_range.text.length) // 将当前textRange的currentPos移动到之前创建的textRange的currentPos处, 此时当前textRange范围变为整个内容的currentPos处到当前范围end处 cur_range.setEndPoint("StartToStart",all_range); // 此时当前textRange的Start到End的长度,就是光标的位置 currentPos = cur_range.text.length; } else { // 文本框获取焦点 _this.focus(); // 获取当前元素光标位置 currentPos = _this.get(0).selectionStart; //console.log("光标当前位置:"+currentPos); } // 返回光标位置 return currentPos; }; // 获取检索值开始位置 oldCurrentPos var getOldCurrentPos = function() { getStart(); // 开始输入的时候的光标位置 currentPos oldCurrentPos = currentPos; // 储存输入开始位置 console.log(oldCurrentPos); } // 结束检索事件 var endSearch = function(){ me.dropdown.empty(); me.dropdown.hide(); searchStart = false; enterCharacter =''; } // 设置光标位置 var setCarePosition = function(start, end) { if (navigator.userAgent.indexOf("MSIE") > -1) { var all_range = ''; if ( _this.get(0).tagName == "TEXTAREA" ) { // 根据body创建textRange all_range = document.body.createTextRange(); // 让textRange范围包含元素里所有内容 all_range.moveToElementText(_this.get(0)); } else { // 根据当前输入元素类型创建textRange all_range = _this.get(0).createTextRange(); } _this.focus(); // 将textRange的start设置为想要的start all_range.moveStart('character',start); // 将textRange的end设置为想要的end. 此时我们需要的textRange长度=end-start; 所以用总长度-(end-start)就是新end所在位置 all_range.moveEnd('character',-(all_range.text.length-(end-start))); // 选中从start到end间的文本,若start=end,则光标定位到start处 all_range.select(); } else { // 文本框获取焦点 _this.focus(); // 选中从start到end间的文本,若start=end,则光标定位到start处 _this.get(0).setSelectionRange(start, end); } }; // 获取检索的值 selctVal var getSelectVal = function(){ var val = me.val(); if( searchStart == true && enterCharacter != opts.levelCharacter ){ // 当输入的是字符 triggerCharacter 的时候 默认为 $ selectVal = val.substring(oldCurrentPos,currentPos); // 检索值直接为获取的文本区域 } if( searchStart == true && enterCharacter == opts.levelCharacter ){ // 当输入的是字符 levelCharacter 的时候 默认为 . dotVal = val.slice(0,currentPos); dotDollerPos = dotVal.lastIndexOf(opts.triggerCharacter)+1; dotSelectVal = dotVal.substring(dotDollerPos,currentPos); selectVal = dotSelectVal; console.log("到当前下标的字符串为:" + dotVal); console.log("到当前下标最近的$下标是:" + dotDollerPos); console.log("输入 . 时检索值为:" + dotSelectVal); } console.log("获取的值区域为:"+oldCurrentPos+"-"+currentPos); if ( oldCurrentPos > currentPos ){ // 回删时清除选项li 隐藏下拉框 endSearch() } } // 选中li当前项 改变输入框value值 定位光标 var changeValue = function() { var val = me.val(); var valLength = val.length; var liTxt = me.dropdown.find(".active").text(); var liTxtLength = liTxt.length; // 此处需要区分触发检索事件的符号是 if ( enterCharacter == opts.levelCharacter ) { // 如果是 . var beforeSelectVal = val.substring(0, dotDollerPos); } else { // 如果是 & var beforeSelectVal = val.substring(0, oldCurrentPos); } var beforeSelectValLength = beforeSelectVal.length; var afterSelectVal = val.substring(currentPos, valLength); var pos = beforeSelectValLength + liTxtLength; val = beforeSelectVal + liTxt + afterSelectVal; _this.val(val); setCarePosition(pos, pos); // 将光标定位在插入值后面 endSearch(); console.log("文本长度:" + beforeSelectVal.length); console.log("li文本为:" + liTxt); console.log("前部为:" + beforeSelectVal); console.log("后部分为:" + afterSelectVal); return false; // 此处必须加上return false 不然会调用callbacktips 初始化 dropdown } // 定义回调函数 callbacktips var callbacktips = function(arr_json) { //1. empty body me.dropdown.empty(); //2. create list if (arr_json) { for (var i = 0; i < arr_json.length; i++) { var n = arr_json[i].indexOf(selectVal); if ( n != -1 ) { me.dropdown.append("
  • " + arr_json[i] + "
  • "); } else{ return; } }; } //3. show and select first me.dropdown.show(); me.dropdown.find("li:first-child").addClass("active"); }; // 点击当前项获取文本值 重组输入框 value值 失去焦点时隐藏下拉框 清空下拉框 $(document).click(function(e) { var e = event || window.event; var el = e.target.localName; // 获取事件源 标签名 el == "li" ? changeValue() : endSearch(); }) // 获得焦点的时候获取光标位置 me.click(function() { getOldCurrentPos() }); //下拉框显示时 阻止键盘方向键默认事件 me.keydown(function(e) { var dropdownIsshow = me.dropdown.css("display"); if (dropdownIsshow == "block") { if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13) { e.preventDefault(); } } }) // 监听输入框value值变化 me.keyup(function(e) { if (e.keyCode >= 37 && e.keyCode <= 40) { reutrn; } var val = me.val(); getStart(); enterCharacter = val.substring(currentPos - 1, currentPos); checkCharacter = true; if (enterCharacter == opts.triggerCharacter || enterCharacter == opts.levelCharacter) { console.log("输入了$或者."); searchStart = true; getOldCurrentPos(); // 输入 $ 的时候重置 oldCurrentPos } getSelectVal(); // 外度调用获取检索值方法 保证实时更新 selectVal 及 searchStart if ( searchStart == true && checkCharacter == true && e.keyCode != 13 ) { console.log("获取的值:"+selectVal); if( $.isFunction(opts.keyPressAction) ){ opts.keyPressAction(selectVal, function(arr_json){ // 调用回调函数 callbacktips(arr_json); }); } } if (e.keyCode == 13) { // 按enter键选取当前li文本值 重组输入框 value值 var dropdownIsshow = _this.dropdown.css("display"); if ( dropdownIsshow == "block" ){ // 为了在下拉框隐藏时按 enter键 能换行,需要加上这个判断 changeValue(); console.log("这是点击enter后searchStart:"+searchStart); } } console.log("这是整个事件执行完成以后:"+searchStart); }); // 切换当前项 me.dropdown.on('mouseenter','li', function() { $(this).addClass("active").siblings().removeClass("active"); }); // 设置显示位置 $(this).keyup(function() { setPopupPos(this); }); // 调用 Teleprompter, 获取光标坐标 function setPopupPos(el) { var cursorPos = Teleprompter.getInputPositon(el); var tleft = parseInt(me.css("marginLeft")); var ttop = parseInt(me.css("marginTop")); var dropdown = me.dropdown.get(0); dropdown.style.left = cursorPos.left - tleft + 'px'; //dropdown.style.top = cursorPos.bottom - ttop + 10 + 'px'; dropdown.style.bottom = "50px"; } }) } }) var defaults = { triggerCharacter : '[', levelCharacter: '.', dropdownWidth:'150px' }; })(window.jQuery);