概述
微博的@辅助输入功能具有方便快捷的特点,基础功能描述为:
- 1.在TextArea内输入“@”时,在光标处弹出选项列表。
- 2.通过键盘或者鼠标选择选项,文本插入到textArea。
人肉相关文章之后才知道:在非ie浏览器下,获得TextArea的光标位置不是一件容易的事情!
目前大家常用的实现思路是:采用一个屏幕外的div模拟TextArea,通用地插入内容,通过div内光标位置来获得textarea内的光标位置。
这篇文章有详细的叙述:
http://yiminghe.iteye.com/blog/1482102
具体技术实现,jquery的找到一些,比如这篇文章:
http://lecoding.com/articles/316.html。
extjs下的实现则几乎没找到,只找到这一篇:
http://www.rahulsingla.com/blog/2010/12/extjs-re-usable-dropdownlist-for-any-form-field
作者是在ext3.1下实现的,对于这个实现我不甚满意:
- 1.用DataView不是好选择
简单的选项可以用Menu,复杂的用ListView,没必要去折腾DataView+Layer组合。
即这个自定义派生类是不必要的。
- 2.该实现并没有完善对有无弹出菜单两种情形下,对键盘事件的处理,连回车键录入都不能支持。
没办法,自己造轮子吧!
综合上述文章,实现思路如下:
- 1.派生ExtJS的TextArea,实现支持@输入功能的增强版TextArea。
- 2.采用ListView(GridPanel)作为弹出菜单,因为它已经支持showAt方法,可以在指定坐标显示。
- 3.采用离屏div获得textarea内的光标坐标。
- 4.为了处理菜单可见/隐藏 两种状态下的键盘事件,结合了extjs的三种键盘处理机制:
listener: "specialkey" "keyup",
以及Ext.KeyNav。
原因是:前两种无法接管“回车”键,而第三种在接管之后,我没有找到何时的方法,将键盘消息传递回textarea,获得相同的效果
(试了fireEvent,没成功。)
- 5.关键点:通过程序语句选择菜单项时 XXX.getSelectionModel().select(0,true,true);
后两个参数必须加上——阻止触发相关event,否则存在性能问题——响应严重延迟。
- 6.代码采用ExtJS4.2,在chrome和firefox下测试通过。
完整代码如下:
Ext.define('Ext.form.TA', { extend: 'Ext.form.TextArea', config: { enableKeyEvents:true, listeners:{ 'specialkey': function(field, e){ this.onKey(e); }, 'keyup':function(ta,e,opt){ var me=this; switch(e.keyCode){ case 50: me.showMenu(); break; } } } }, showMenu:function(){ var ta = this; var pos = ta.inputEl.dom.selectionStart; var txt = ta.getRawValue( ).substring(0,pos); var p0 = ta.getXY(); var x = p0[0]+ta.getDivWidth(txt); var y = p0[1]+ta.getDivHeight(txt); //contactsDropDown.bindElement(ta.el); var mnu = this.mnu; mnu.showAt([x,y]); mnu.getSelectionModel().select(0,true,true); this.focus(); }, hideMenu:function(){ this.mnu.hide(); }, constructor: function (config) { config.width =config.width||400; config.height =config.height||200; this.callParent(arguments); }, getDivHeight:function(text){ this.heightDiv.setHTML(text.replace(/n/g, '<br/>')); return this.heightDiv.getHeight() + 10; }, getDivWidth:function(text){ var h = this.getDivHeight('x'); var olen = this.getDivHeight(text); if (h != olen) { for (var i = 0; i < text.length; ++i) { var s = text.substr(0, text.length - i); var len = this.getDivHeight(s); if (len != olen) { text = text.substr(text.length - i); break; } } } this.widthDiv.setHTML(text); return this.widthDiv.getWidth(); }, buildMenu:function(){ var me=this; me.ds = Ext.create('Ext.data.Store', { storeId:'simpsonsStore', fields:['name', 'email', 'phone'], data:{'items':[ { 'name': 'Lisa', "email":"lisa@simpsons.com", "phone":"555-111-1224" }, { 'name': 'Bart', "email":"bart@simpsons.com", "phone":"555-222-1234" }, { 'name': 'Homer', "email":"home@simpsons.com", "phone":"555-222-1244" }, { 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254" } ]}, proxy: { type: 'memory', reader: { type: 'json', root: 'items' } } }); me.mnu=Ext.create('Ext.grid.Panel', { floating:true, //title: 'Simpsons', selModel: 'SINGLE', store: Ext.data.StoreManager.lookup('simpsonsStore'), columns: [ { text: 'Name', dataIndex: 'name' }, { text: 'Email', dataIndex: 'email', flex: 1 }, { text: 'Phone', dataIndex: 'phone' } ], height: 200, width: 400 }); me.mnu.on("itemclick",function(gd, record, item, index, e, eOpts ){ me.onSelOk(); }); }, insertText:function(val){ var value=val=='rn'?val:val+' '; var ta = this.inputEl.dom; var oriValue = this.getValue(); var pos_start = ta.selectionStart; this.setValue(oriValue.substring(0,ta.selectionStart) + value + oriValue.substring(ta.selectionEnd)); //ta.setValue(oriValue.toString() + value ); ta.selectionStart = pos_start+value.length; ta.selectionEnd = ta.selectionStart; //this.focus(); }, onSelOk:function(){ var sel = this.mnu.getSelectionModel(); var si = sel.getSelection(); if(!si || si.length==0) return; this.insertText(si[0].data['name']); this.hideMenu(); }, onKey: function(e){ if(!this.mnu.isVisible()){ if(e.getKey()==e.ENTER){ this.insertText('rn'); } return; } e.stopEvent(); // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN, // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN var sel = this.mnu.getSelectionModel(); var si = sel.getSelection(); var pos = 0; if(si && si.length>0){ pos = si[0].index; } var mnu = this.mnu; switch(e.getKey()){ case e.DOWN: pos++ if(pos>=this.ds.getCount()) pos=0; this.mnu.getSelectionModel().select(pos,true,true); break; case e.UP: pos--; if(pos<0) pos=this.ds.getCount()-1; this.mnu.getSelectionModel().select(pos,true,true); break; case e.ESC: this.hideMenu(); break; case e.ENTER: this.onSelOk(); break; } //this.focus(); }, buildKey:function(){ this.keyNav = new Ext.KeyNav(this.inputEl, { "enter": function(e) { console.log('enter'); this.onKey(e); }, "esc": function(e) { console.log('esc'); this.onKey(e); }, "tab": function(e) { console.log('tab'); this.onKey(e); }, scope: this }); }, onRender: function () { this.callParent(); this.heightDiv = Ext.DomHelper.append(document.body,{tag:'div',id:'height_div',style:'visibility:hidden;position:absolute;z-index:10000;top:0px;left:3000px;word-break:break-all;word-wrap:break-word'},true); this.heightDiv.setWidth(this.getWidth()); this.heightDiv.setStyle('font-size',this.inputEl.getStyle('font-size')); this.widthDiv = Ext.DomHelper.append(document.body,{tag:'div',id:'width_div',style:'visibility:hidden;position:absolute;z-index:10000;top:0px;left:3000px;'},true); this.widthDiv.setHeight(this.getHeight()); this.widthDiv.setStyle('font-size',this.inputEl.getStyle('font-size')); this.buildMenu(); this.buildKey(); } });
附@弹出效果截图:
最后
以上就是刻苦啤酒为你收集整理的微博@输入功能的ExtJS实现的全部内容,希望文章能够帮你解决微博@输入功能的ExtJS实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复