|
(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 = '<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;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(/>/g, '>').replace(/\n/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 = 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 = $('<ul class="tips" style="display:none;"></ul>');
|
|
$("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("<li class='tips-item'>" + arr_json[i] + "</li>");
|
}
|
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);
|