[求助]如何用JQuery来实现Ctrl+Space完成输入特定字符提示的自动完成功能

贾柯 2012-02-04 05:55:39
例,在文本框textarea中输入字符串"<设计"后,按下Ctrl+Space键,自动提示出与"<设计"有关的下拉框关联文字内容,然后选择其中一个后,能自动加上关闭符'>'。接着在同一行文字后面,当输入字符串"[保险"后,在次按下Ctrl+Space键,自动提示出与"[保险"有关的下拉框关联表名的文字内容,能自动加上关闭符']'。

例如,输入内容:在<设计模版>里选择一个模版后,在[保险经纪人]表中选择一个经纪人。
在这里输入"<设计"后,按下Ctrl+Space键,弹出一个下拉框提示,选择'设计模版',之后接着在"[保险"后,在按下Ctrl+Space键,弹出一个下拉框提示,选择'保险经纪人',就这样的效果。并且光标置放在封闭符后面时,按下Backspace键后,仅删除"<设计模版>"或"[保险经纪人]"的自动提示内容。

这个就好象在Eclipse的代码框中输入"Obj."后,按下Ctrl+Space键,自动弹出一个下拉框,里面是Obj相关的属性和方法等。
不过我这个要求比较简单,提示的只是与"<设计"有关联的文字内容。例如:"<设计书>"、"<设计模版>"、"<设计方案>"等。
这里有一个特殊的地方,是我觉得不好实现的,那就是,当前面第一个"<设计"的自动完成实现后,后面第二个"[保险"的自动完成该如何实现呢?

以上"<设计"、"[保险"提示出的下拉列表的内容存放在一个MapList中。

不知上面我所说的功能通过JQuery该如何实现,请教这里的大虾们了。
可能的话能把示例代码发到我邮箱吗?hnjyfxl@gmail.com
不胜感激!!!
...全文
174 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
贾柯 2012-02-12
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 ttwdr 的回复:]
到此结束,LZ 考虑结帖给分吧 ;-)
[/Quote]

非常感谢你的解答,也很受益。 所有分都给你,必需的!
不过我昨天已经用JQuery的autocomplete插件达到了同样的效果。
但还是要非常感谢你的回复,因为我在扩展autocomplete.js时用到了你写的光标定位技术。
接下来我还有个正则表达式的问题要解决了。
ttwdr 2012-02-11
  • 打赏
  • 举报
回复
到此结束,LZ 考虑结帖给分吧 ;-)
ttwdr 2012-02-11
  • 打赏
  • 举报
回复

//以下部分代码出处:http://blog.csdn.net/kingwolfofsky/article/details/6586029
var kingwolfofsky = {
/**
* 获取输入光标在页面中的坐标
* @param {HTMLElement} 输入框元素
* @return {Object} 返回left和top,bottom
*/
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
};
}
};

function getPosition(ctrl) {
var p = kingwolfofsky.getInputPositon(ctrl);
document.getElementById('show').style.left = p.left + "px";
document.getElementById('show').style.top = p.bottom + "px";
}
</script>
<textarea id="ta1" style="width: 320px; height: 240px;" onkeydown="ta1_keydown()" onfocus="ta1_focus()">测试 现在如何让这个 "列表框 "显示在textarea文本框前面? 测试 而且不夺走文本框的焦点。</textarea>
ttwdr 2012-02-11
  • 打赏
  • 举报
回复
成了!用 textarea,LZ 自己再修修 Bug 和跨浏览器的情况就好了。代码太长,分两层楼贴上来:
function ta1_focus()
{
var sel = document.getElementById("sel1");
if(sel != null && sel.outerHTML != null)
{
sel.outerHTML = "";
}
}

function ta1_keydown()
{
if(event.keyCode == 113) //F2
{
var ta = event.srcElement;
ta.focus();
savePos(ta);
var _range = document.selection.createRange();
if(_range.text.length > 0); //不选中文本
{
_range.text = _range.text;
_range.select();
}

var p = kingwolfofsky.getInputPositon(ta);

var ptxt = ta.innerText.substr(0, start);
var _reg = new RegExp(test_txt + "$", "g");
if(_reg.test(ptxt))
{
var sel = document.createElement("SELECT");
sel.id = "sel1";
document.body.appendChild(sel);
sel.style.position = "absolute";
sel.style.left = p.left + "px";
sel.style.top = p.top + 12 + "px";
sel.options.add(new Option("测试文本1", "测试文本1", true, true));
sel.options.add(new Option("测试文本2", "测试文本2"));
sel.size = 2;

ta.contentEditable = "false";
sel.focus();
sel.onclick = seltext;
sel.onkeydown = caltext;
}
}
}

var test_txt = "测试";

function seltext()
{
if(test_txt == null || test_txt.length < 1)
{
return;
}
var sel = event.srcElement;
var _text = sel.value;
sel.outerHTML = "";
var ta = document.getElementById("ta1");
var pre = ta.value.substr(0, start - test_txt.length);
var post = ta.value.substr(start);
ta.innerText = pre + _text + post;

ta.contentEditable = "true";
ta.focus();
var _range = ta.createTextRange();
_range.moveStart("character", start + (_text.length - test_txt.length));
_range.collapse(true);
_range.select();
}

function caltext()
{
if(event.keyCode == 27) //ESC
{
var sel = event.srcElement;
sel.outerHTML = "";

var ta = document.getElementById("ta1");
ta.contentEditable = "true";
ta.focus();
var _range = ta.createTextRange();
_range.moveStart("character", start);
_range.collapse(true);
_range.select();
}
}

//以下部分代码出处:http://blog.csdn.net/liujin4049/article/details/1244065
var start=0;
var end=0;
function savePos(textBox)
{
start = 0;
end = 0;
if(typeof(textBox.selectionStart) == "number") //Firefox
{
start = textBox.selectionStart;
end = textBox.selectionEnd;
}
else if(document.selection) //IE
{
var range = document.selection.createRange();
if(range.parentElement().id == textBox.id)
{
// create a selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(textBox);
for(start = 0; range_all.compareEndPoints("StartToStart", range) < 0; start++)
{
range_all.moveStart('character', 1);
}
for (var i = 0; i <= start; i ++)
{
if(textBox.value.charAt(i) == '\n')
start++;
}
var range_all = document.body.createTextRange();
range_all.moveToElementText(textBox);
for(end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; end ++)
{
range_all.moveStart('character', 1);
}
for(var i = 0; i <= end; i ++)
{
if(textBox.value.charAt(i) == '\n')
end ++;
}
}
}
}
贾柯 2012-02-09
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 ttwdr 的回复:]

试着做了个,有几个问题:
1. 不能用 textarea,因为其是文本控件,无法插入非文本节点。所以用 div 的 contentEditable 模式代替,用样式模仿 textarea;当然,LZ自己再花点功夫,在运行时把 div 和 textarea 进行实时互换应该也是可以的。
2. 如 #3 楼所说,Ctrl+Space键与输入法快捷键有冲突,所以随便选了一个键 F2 作为代替。弹出……
[/Quote]

你的方法确实能实现我想要的功能,可是上面有要求一定要在textarea框里面完成下拉框的关联。不用<div>真的不能实现了吗?有没有人用JQuery的插件实现过这个功能呢?这个好像应该可以做到的吧。目前我还在调查。纠结郁闷中,唉。。。
ttwdr 2012-02-08
  • 打赏
  • 举报
回复
试着做了个,有几个问题:
1. 不能用 textarea,因为其是文本控件,无法插入非文本节点。所以用 div 的 contentEditable 模式代替,用样式模仿 textarea;当然,LZ自己再花点功夫,在运行时把 div 和 textarea 进行实时互换应该也是可以的。
2. 如 #3 楼所说,Ctrl+Space键与输入法快捷键有冲突,所以随便选了一个键 F2 作为代替。弹出下拉列表后可按 ESC 取消;
3. 以下代码在 Win7 IE9 下调试通过,LZ 要想跨浏览器还要自己再花点功夫,估计 bug 也是要花不少功夫整理的。
代码:
<script type="text/javascript">
function ta1_focus()
{
var ta = event.srcElement;
ta.contentEditable = "true";
}
function ta1_keydown()
{
if(event.keyCode == 113) //F2
{
var ta = event.srcElement;
ta.focus();
savePos(ta);
var _range = document.selection.createRange();
if(_range.text.length > 0); //不选中文本
{
_range.text = _range.text;0
_range.select();
}

var ptxt = ta.innerText.substr(0, start);
var _reg = new RegExp(test_txt + "$", "g");
if(_reg.test(ptxt))
{
test_txt = "测试";
document.execCommand("InsertSelectListbox",false,"sel1");
var sel = document.getElementById("sel1");
var _left = sel.offsetLeft;
var _top = sel.offsetTop;
sel.style.position = "absolute";
sel.style.left = _left + "px";
sel.style.top = _top + 12 + "px";

sel.options.add(new Option("测试文本1", "测试文本1", true, true));
sel.options.add(new Option("测试文本2", "测试文本2"));

ta.contentEditable = "false";
sel.focus();
sel.onclick = seltext;
sel.onkeydown = caltext;
}
}
}
var test_txt = "测试";
function seltext()
{
if(test_txt == null || test_txt.length < 1)
{
return;
}
var sel = event.srcElement;
var _text = sel.value;
sel.outerHTML = "";
var ta = document.getElementById("ta1");
var pre = ta.innerText.substr(0, start - test_txt.length);
var post = ta.innerText.substr(start);
ta.innerText = pre + _text + post;

ta.contentEditable = "true";
ta.focus();
var _range = document.selection.createRange();
_range.moveStart("character", start + (_text.length - test_txt.length));
_range.select();
}
function caltext()
{
if(event.keyCode == 27) //ESC
{
var sel = event.srcElement;
sel.outerHTML = "";

var ta = document.getElementById("ta1");
ta.contentEditable = "true";
ta.focus();
var _range = document.selection.createRange();
_range.moveStart("character", start);
_range.select();
}
}
//以下部分代码出处:http://blog.csdn.net/liujin4049/article/details/1244065
var start=0;
var end=0;
function add()
{
var textBox = document.getElementById("ta1");
var pre = textBox.innerText.substr(0, start);
var post = textBox.innerText.substr(end);
textBox.value = pre + document.getElementById("inputtext").value + post;
}

function savePos(textBox)
{
start = 0;
end = 0;
if(typeof(textBox.selectionStart) == "number") //Firefox
{

start = textBox.selectionStart;
end = textBox.selectionEnd;
}
else if(document.selection) //IE
{
var range = document.selection.createRange();
if(range.parentElement().id == textBox.id)
{
// create a selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(textBox);
for (start = 0; range_all.compareEndPoints("StartToStart", range) < 0; start++)
range_all.moveStart('character', 1);
for (var i = 0; i <= start; i ++)
{
if(textBox.innerText.charAt(i) == '\n')
start++;
}
var range_all = document.body.createTextRange();
range_all.moveToElementText(textBox);
for(end = 0; range_all.compareEndPoints('StartToEnd', range) < 0; end ++)
range_all.moveStart('character', 1);
for(var i = 0; i <= end; i ++)
{
if(textBox.innerText.charAt(i) == '\n')
end ++;
}
}
}
}
</script>
<div id="ta1" style="width: 320px; height: 240px; font-size: 12px; border: 2px InactiveBorder inset; overflow-y: scroll;"
contentEditable ="true" onkeydown="ta1_keydown()" onfocus="ta1_focus()">
测试 现在如何让这个 "列表框 "显示在textarea文本框前面?测试
而且不夺走文本框的焦点。
</div>
贾柯 2012-02-08
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 ttwdr 的回复:]

1.表单中定位光标的问题,IE下勉强有相关的功能,可以定位光标的字符位置,但没有直接的方法取得坐标(不嫌麻烦的话,可以根据光标的字符位置、表单的大小、以及字体定义推算出坐标);其它浏览器则连光标都难以取到。因此,web 页面上其实是避免这样设计的。如果,一定要达到这样的效果,应当借助第三方插件来实现(flash、ActiveX、Java)。
2.判断输入内容,完全可以用正则表达式来过滤,可以作……
[/Quote]

[Quote=引用 9 楼 laidezhong 的回复:]

很明显 不能用textarea
[/Quote]


这个必须得用,能搞得定么?

现在如何让这个 "列表框 "显示在textarea文本框前面?
而且不夺走文本框的焦点。
laidezhong 2012-02-08
  • 打赏
  • 举报
回复
很明显 不能用textarea
ttwdr 2012-02-07
  • 打赏
  • 举报
回复
1.表单中定位光标的问题,IE下勉强有相关的功能,可以定位光标的字符位置,但没有直接的方法取得坐标(不嫌麻烦的话,可以根据光标的字符位置、表单的大小、以及字体定义推算出坐标);其它浏览器则连光标都难以取到。因此,web 页面上其实是避免这样设计的。如果,一定要达到这样的效果,应当借助第三方插件来实现(flash、ActiveX、Java)。
2.判断输入内容,完全可以用正则表达式来过滤,可以作到非常强大。
贾柯 2012-02-07
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 mengnanleo 的回复:]

只是麻烦而已,不难。。。

引用 5 楼 ttwdr 的回复:

引用 4 楼 hnjyfxl 的回复:

引用 2 楼 ttwdr 的回复:
这个其实不难搞,只是费些功夫,LZ的诱惑太低了 ;-p


你搞出来给我看看,这个没那么好搞的。

难者不会,会者不难。要实现,工作量比较大倒是真的。
[/Quote]

有2个问题请教。
1.在textarea框内,如何在定位的光标位置处,压下快捷键后弹出下拉菜单。
2.押下快捷键后,如何区分你输入的特定内容,并弹出自动完成下拉菜单列表。
希望能得到大侠们的赐教。
mengnanleo 2012-02-07
  • 打赏
  • 举报
回复
只是麻烦而已,不难。。。

[Quote=引用 5 楼 ttwdr 的回复:]

引用 4 楼 hnjyfxl 的回复:

引用 2 楼 ttwdr 的回复:
这个其实不难搞,只是费些功夫,LZ的诱惑太低了 ;-p


你搞出来给我看看,这个没那么好搞的。

难者不会,会者不难。要实现,工作量比较大倒是真的。
[/Quote]
ttwdr 2012-02-06
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 hnjyfxl 的回复:]

引用 2 楼 ttwdr 的回复:
这个其实不难搞,只是费些功夫,LZ的诱惑太低了 ;-p


你搞出来给我看看,这个没那么好搞的。
[/Quote]
难者不会,会者不难。要实现,工作量比较大倒是真的。
贾柯 2012-02-06
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 ttwdr 的回复:]
这个其实不难搞,只是费些功夫,LZ的诱惑太低了 ;-p
[/Quote]

你搞出来给我看看,这个没那么好搞的。
laidezhong 2012-02-06
  • 打赏
  • 举报
回复
Ctrl + Space 容易与输入法切换 冲突 建议换一个
ttwdr 2012-02-05
  • 打赏
  • 举报
回复
这个其实不难搞,只是费些功夫,LZ的诱惑太低了 ;-p
贾柯 2012-02-05
  • 打赏
  • 举报
回复
看来这个问题是没人能解了,我心目中的Web达人在哪里?
如果有谁能搞定这个,7K的薪水来我公司没有问题。
要求3年工作经验。

87,910

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧