`

微博@输入功能的ExtJS实现

 
阅读更多

微博的@辅助输入功能具有方便快捷的特点,基础功能描述为:

  • 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,在chromefirefox下测试通过。

完整代码如下:

 

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=='\r\n'?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('\r\n');
  	 		}
  	 		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();
   }
});

 

附@弹出效果截图:

 

 

  

  • 大小: 89.8 KB
0
0
分享到:
评论

相关推荐

    @@@extjs+struts2+json plugin的例子

    在IT行业中,构建Web应用程序是一项常见的任务,而`ExtJS`、`Struts2`和`JSON`是其中的关键技术,常被用来创建交互性强、功能丰富的用户界面和高效的服务器通信。下面将详细阐述这三个技术及其结合使用的情况。 ...

    ExtjS实现聊天功能

    在这个"ExtJS实现聊天功能"的项目中,我们将探讨如何利用ExtJS框架构建一个类似于QQ的聊天应用程序。 首先,我们要理解聊天功能的核心要素。一个基本的聊天应用通常包含以下部分: 1. **用户界面**:ExtJS提供了...

    Extjs实现用户登录

    在本文中,我们将深入探讨如何使用ExtJS框架实现一个用户登录功能。ExtJS是一个流行的JavaScript库,用于构建富客户端Web应用程序。以下是如何使用ExtJS创建一个简单的登录界面和处理登录逻辑的步骤。 首先,我们来...

    extjs实现的带标签、翻页动画的书

    在本项目中,“extjs实现的带标签、翻页动画的书”显然利用了ExtJS的组件化特性和动画功能,创建了一个模拟真实书籍阅读体验的应用。 首先,我们来看看“标签”这一概念。在Web应用中,标签(Tab)通常用来组织和...

    extjs实现增删查改

    在“extjs实现增删查改”这个主题中,我们将探讨如何使用ExtJS来实现基本的数据操作功能。 首先,增删查改(CRUD,Create, Read, Update, Delete)是任何数据管理应用的核心功能。在ExtJS中,我们可以利用Grid ...

    Extjs 轻松实现下拉框联动

    最近小弟做了Extjs实现实现下拉框联动的效果,参考了好久才学会,闲下来发一个简单的例子。。呵呵

    Extjs实现的聊天室

    在本项目中,“Extjs实现的聊天室”利用了ExtJS的组件化特性和WebSocket技术,构建了一个实时的在线聊天平台。 WebSocket是一种在客户端和服务器之间建立长连接的协议,允许双方进行全双工通信,即数据可以在任意...

    extjs实现动态树

    在ExtJS中,这种功能主要通过`Ext.tree.TreePanel`或其现代版本`Ext.tree.View`来实现。 一、ExtJS 动态树基础 1. `Ext.tree.TreePanel`:这是ExtJS中用于创建树形结构的主要组件。它具有丰富的配置项和事件,支持...

    ExtJS实现Excel的导出功能(poi)

    在本文中,我们将探讨如何使用ExtJS框架配合Apache POI库来实现Excel的导出功能。ExtJS是一个强大的JavaScript库,专门用于构建富客户端应用程序,它提供了丰富的组件和功能,而无需关注后端技术。Apache POI是Java...

    ExtJS 实现动态处理加载 grid 实现了CRUD还有分页功能

    在本示例中,我们将深入探讨如何使用ExtJS实现动态加载grid,同时实现CRUD(创建、读取、更新、删除)操作和分页功能。 首先,让我们了解什么是Grid。在ExtJS中,Grid是一种数据网格组件,用于展示大量结构化数据。...

    extjs实现报表

    - **打印支持**:使用ExtJS的Print Manager或第三方库实现报表的打印功能。 - **数据导出**:将报表数据导出为Excel、CSV或其他格式,便于进一步处理。 9. **性能优化** - **远程排序和过滤**:减轻客户端负担,...

    Extjs 实现增删改查

    Extjs 实现增删改查,可以与后台代码结合起来,利于实现

    extjs实现权限拦截

    在“ExtJS实现权限拦截”的主题中,我们将深入探讨如何结合这两个技术来实现用户登录验证以及基于角色的权限控制。 1. **ExtJS 登录界面** 在前端,ExtJS可以创建美观且功能丰富的登录界面。通过使用FormPanel组件...

    extjs 实现动态表头

    标题“extjs 实现动态表头”表明我们要讨论的是如何在ExtJS环境中构建一个能够动态改变列头的表格。动态表头的核心在于理解ExtJS的GridPanel组件和ColumnModel类。GridPanel用于显示数据,而ColumnModel则定义了...

    extjs实现jbpm工作流流程设计

    这部分代码值得学习和参考,以了解如何在EXTJS中实现类似的功能。 在实现过程中,开发者需要注意以下几点: 1. 数据模型的设计:将jbpm的工作流模型映射到EXTJS的数据模型,确保数据在前端和后端之间能够正确同步。...

    extjs实现登陆验证

    从初始化组件、构建表单到处理用户输入,每一步都体现了ExtJS在Web应用开发中的强大功能和灵活性。理解这些代码背后的逻辑,不仅能够帮助开发者快速搭建登录界面,还能够深入掌握ExtJS框架的使用技巧,提升前端开发...

    Extjs 下拉菜单实现拼音输入进行检索

    Extjs 下拉菜单实现拼音输入进行检索

    extjs实现下拉框多选

    在本篇中,我们将深入探讨如何在ExtJS中实现下拉框的多选功能。 首先,了解基本的ExtJS ComboBox。ComboBox 是一个具有搜索功能的下拉列表,用户可以从中选择一个选项。它由两个主要部分组成:一个可编辑的输入字段...

    ExtJS实现文件下载

    在本文中,我们将深入探讨如何使用ExtJS框架来实现文件下载功能。ExtJS是一个流行的JavaScript库,用于构建桌面级的Web应用程序,它提供了丰富的组件和功能,包括与服务器的交互,如文件下载。 首先,我们要了解`...

    extjs实现用户登录界面

    在“extjs实现用户登录界面”这个主题中,我们将深入探讨如何利用ExtJS创建一个功能完善的登录界面。 首先,登录界面通常包含用户名、密码输入框以及登录按钮。在ExtJS中,这些可以通过创建`Ext.form.Panel`来实现...

Global site tag (gtag.js) - Google Analytics