웹 프론트엔드 H5 튜토리얼 详解微博发言框的@功能

详解微博发言框的@功能

May 17, 2016 am 09:09 AM

经常使用微博的人会发现,当我们在输入框输入@然后敲一个人的名字,会弹出一个tip提示层,如图所示:

weibo.jpg



出于对这个功能的好奇,并抱着学习的态度,翻阅了一些资料后对这个Javascript进行了探讨和研究。
对这个功能进行分析如下:

1、确定光标的位置

2、textarea文本框里对字符串@的判断

3、tip的弹出事件

4、键盘的操作事件

5、ajax调用

6、文字的插入

......

当然还有其他的功能。

看着是不是感觉很复杂?没关系,我们一步一步的分析。

首先我们要确定textarea的光标位置。在W3C中,获取光标位置比较简单,可以用selectionStart和selectionEnd,IE浏览器不支持这2个属性 ,但是IE又一个document.selection对象,可以模拟实现相同的功能。代码如下:


  1. //先定义一个基本类,来设置一些全局变量
  2. function demonAt(opts) {
  3.     this.elem=opts.elem; //文本框
  4.     this.at= {};    //临时保存文本框内容截取属性
  5.     this.opt= {};
  6.     this.searched=""; //用于判断用户输入字符是否和前面一样,如果一样跳过ajax
  7.     this.url=opts.url;
  8.     this.index=1;
  9. }
  10. //微博的@功能
  11. demonAt.prototype= {
  12.     getCursor: function(elem) {
  13.         var _this=this;
  14.         var rangeData = {
  15.             start: 0,
  16.             end: 0,
  17.             text: ""
  18.         };
  19.         if(typeof(this.elem.selectionStart)=="number") {//W3C
  20. rangeData.start=this.elem.selectionStart;//光标起始位置
  21.             rangeData.end=this.elem.selectionEnd;//光标末尾位置
  22. rangeData.text=this.elem.value.substring(0,this.elem.selectionStart);//获取文本框value
  23.         } else if (document.selection) {//IE
  24.             var sRange=document.selection.createRange();
  25.             var oRange=document.body.createTextRange();
  26.             oRange.moveToElementText(this.elem);
  27.             rangeData.text=sRange.text;
  28.             rangeData.bookmark = sRange.getBookmark();
  29. for(i=0;oRange.compareEndPoints("StartToStart",sRange)
  30.                 if (this.elem.value.charAt(i) == '\r') {
  31.                     i ++;//IE的特殊处理,遇到enter键需要加1
  32.                 }
  33.             }
  34.             rangeData.start=i;
  35.             rangeData.end = rangeData.text.length + rangeData.start;
  36.             rangeData.text=this.elem.value.substring(0,i);
  37.         }
  38.         //alert(rangeData.text)
  39.         return rangeData;
  40.     },
  41.     setCursor: function(elem,start,end) {//设置光标
  42.         if(this.elem.setSelectionRange) {//W3C
  43.             this.elem.setSelectionRange(start,end);
  44.         } else if(this.elem.createRange) {//IE
  45.             var range=this.elem.createRange();
  46.             if(this.elem.value.length==rangeData.start) {
  47.                 range.collapse(false);
  48.                 range.select();
  49.             } else {
  50.                 range.moveToBookmark(rangeData.bookmark);
  51.                 range.select();
  52.             }
  53.         }
  54.     },
  55.     add: function(elem,txtData,nStart, nLen) {//插入文本参数操作的元素,数据,起始坐标位置,用户输入字符长度
  56.         //this.setCursor(this.elem,this.rangeData);
  57.         this.elem.focus();
  58.         var _range;
  59.         if(this.elem.setSelectionRange) {//W3C
  60.             _tValue=this.elem.value;//获取文本框内容
  61.             var _start = nStart - nLen,//设置光标起点光标的位置-离@的文本长度
  62.             _end = _start + txtData.length,//设置光标末尾,start+数据文字长度
  63.             _value=_tValue.substring(0,_start)+txtData+" "+_tValue.substring(nStart, this.elem.value.length);
  64.             this.elem.value=_value;
  65.             this.setCursor(this.elem,_end+1,_end+1);
  66.         } else if(this.elem.createTextRange) {
  67.             _range=document.selection.createRange();
  68.             _range.moveStart("character", -nLen);//移动光标
  69.             _range.text = txtData+" ";
  70.         }
  71.     }
  72. }
复制代码


自定义一个rangeData对象,保存光标的位置和textarea框内从光标位置到开始处的字符串;返回出来。这个对象在下面其他函数中会用到。根据光标位置的确定,可以书写文字插入函数add();有了上面的函数,我们可以对textarea框内@的字符判断,然后实现tip层定位和弹出,如果判断这个,我们可以用正则:

  1. var _reg=/@[^@\s]{1,20}$/g;
复制代码


那么定位呢,若在textarea内判断是不现实的,因为我们无法获取正确的left和top值,所以这里需要模拟一个div层,将div插入到body 中,定位到与textarea相同的位置,然后获取到textarea内的文字,进行字符串的拆分,加上标签元素,这样可以获取到正确的位置。说的有点绕了,看下面代码能更直观的表达。

  1. var _string=""+"@前面的文字"+""+"@"+""+"@后面的文字"+"";
复制代码


看到这句,很多人应该理解做法,将这段append到上诉定位的div中,这样,我们可以通过标签获取到offset值了。于是我们写下面的代码:


  1. demonAt.prototype= {
  2.     init: function() {//首先我们要初始化
  3.         var _body=$("body");
  4.         var _div=$("
    "),
  5.         _tip=$("
    ");
  6.         _body.append(_div);
  7.         _body.append(_tip);
  8.         var _left=$(this.elem).offset().left+"px",
  9.         _top=$(this.elem).offset().top+"px",
  10.         _width=$(this.elem).outerWidth()+"px",
  11.         _height=$(this.elem).outerHeight()+"px",
  12.         _lineHeight=$(this.elem).css("line-height"),
  13.         _style="position:absolute;overflow:hidden;z-index:-9999;line-height:"+_lineHeight+";width:"+_width+";height:"+_height+";left:"+_left+";top:"+_top;
  14.         _div.attr("style",_style);
  15.         this.inset();
  16.     },
  17.     getAt: function() {
  18.         var _rangeData=this.getCursor();
  19.         var k=_value=_rangeData.text.replace(/\r/g,"");//去掉换行符
  20.         var _reg=/@[^@\s]{1,20}$/g;//正则,获取value值后末尾含有@的并且在20字符内
  21.         var _string="";
  22.         if(_value.indexOf("@")>=
  23.         0&&_value.match(_reg)) {
  24.             var _postion=_rangeData.start;
  25.             var _oValue=_value.match(_reg)[0];//找到value中最后匹配的数据
  26.             var vReg=new RegExp("^"+_oValue+".*$","m");//跟数据匹配的正则   暂时保留
  27.             _value=_value.slice(0, _postion); //重写_value 字符串截取  从0截取到光标位置
  28.             if(/^@[a-zA-Z0-9\u4e00-\u9fa5_]+$/.test(_oValue)&& !/\s/.test(_oValue)) {
  29.                 this.at['m'] = _oValue = _oValue.slice(1);//用户输入的字符  如@颓废小魔,即"颓废小魔"
  30.                 this.at['l'] = _value.slice(0, -_oValue.length - 1); //@前面的文字
  31.                 this.at['r'] = k.slice(_postion - _oValue.length, k.length);//@后面的文字
  32.                 this.at['pos']=_postion;//光标位置
  33.                 this.at['len']=_oValue.length;//光标位置至@的长度,如 @颓废小魔,即"颓废小魔"的长度
  34.                 this.showTip(this.url)
  35.             } else {//alert(1)
  36.                 this.hiddenTip()
  37.             }
  38.         } else {
  39.             this.hiddenTip()
  40.         }
  41.     },
  42.     buidTip: function() {//创建tip,设置tip的位置
  43.         var _this=this;
  44.         $("#tWarp").empty();
  45.         var _string=""+this.format(this.at['l'])+""+"@"+""+this.format(this.at['r'])+"";
  46.         $("#tWarp").html(_string);
  47.         var _left=$("#tWarp cite").offset().left+"px",
  48.         _top=$("#tWarp cite").offset().top+parseInt($("#tWarp").css("line-height"))+"px";
  49.         if(parseInt(_top)>parseInt($("#tWarp").offset().top+$("#tWarp").height())) {
  50.             _top=$("#tWarp").offset().top+$("#tWarp").height()+"px";
  51.         }
  52.         $("#tipAt").css({
  53.             "left":_left,
  54.             "top":_top,
  55.             "display":"block"
  56.         });
  57.         $("#tipAt li").eq(1).addClass("on").siblings().removeClass("on");
  58.         _this.hover();
  59.         //取消keyup绑定,绑定keydown,键盘操作选择,避免与文本框的事件冲突
  60.         $(_this.elem).unbind('keyup').bind('keydown', function(e) {
  61.             return _this.keyMove(e);
  62.         });
  63.     },
  64.     hiddenTip: function() {
  65.         var _this=this;
  66.         $("#tipAt").css("display","none");
  67.         $("#tipAt li").unbind("click,mouseover");
  68.     }
  69. }
复制代码


然后我们添加键盘的操作,这里注意的是,我们在textarea输入文字的时候已经绑定keyup事件,为了避免事件多次绑定,tip的选择我们用keydown事件处理。

  1. demonAt.prototype= {
  2.     keyMove: function(e) {//键盘操作
  3.         var _this=this;
  4.         var _key=e.keyCode;
  5.         var _len=$("#tipAt li").length;
  6.         switch(_key) {
  7.             case 40:
  8.                 //下
  9.                 _this.index++;
  10.                 if(_this.index>_len-1) {
  11.                 _this.index=1;
  12.                 }
  13.                 _this.keyMoveTo(_this.index);
  14.                 //return false一定要加上,不然JS会继续进行调用keyHandler,从而绑定了keyup事件影响到键盘的keydown事件
  15.                 return false;
  16.                 break;
  17.             case 38:
  18.                 //上
  19.                 _this.index--;
  20.                 if(_this.index
  21.                 _this.index=_len-1;
  22.                 }
  23.                 _this.keyMoveTo(_this.index);
  24.                 return false;
  25.                 break;
  26.             case 13:
  27.                 //enter键
  28.                 var txtData=$(".on").text();
  29.                 _this.add(_this.elem,txtData,_this.at['pos'],_this.at['len'])
  30.                 _this.keyHandler()
  31.                 return false;
  32.                 break;
  33.             default:
  34.         };
  35.         _this.keyHandler();
  36.     },
  37.     keyHandler: function() {
  38.         var _this=this;
  39.         _this.index=1;
  40.         //enter键盘操作后重新绑定keyup
  41.         $(_this.elem).unbind("keydown").bind("keyup", function() {
  42.             _this.getAt();
  43.         })
  44.     },
  45.     keyMoveTo: function(index) {
  46.         $("#tipAt li").removeClass("on").eq(index).addClass("on");
  47.     }
  48. }
复制代码


然后添加tip的点击事件和hover事件。

  1. demonAt.prototype= {
  2.     inset: function() {//给li绑定事件,
  3.         var _this=this;
  4.         $("#tipAt").delegate("li","click", function() {//事件委托
  5.             if($(this).index()==0) {
  6.                 _this.elem.focus();
  7.                 return false;
  8.             } else {
  9.                 var txtData=$(this).text();
  10.                 _this.add(_this.elem,txtData,_this.at['pos'],_this.at['len'])
  11.                 _this.hiddenTip()
  12.             }
  13.         })
  14.     },
  15.     hover: function() {
  16.         //hover事件
  17.         var _this=this;
  18.         $("#tipAt li:not(:first)").hover( function() {
  19.             _this.index=$(this).index();
  20.             $(this).addClass("hover").siblings().removeClass("on hover")
  21.         }, function() {
  22.             $(this).removeClass("hover");
  23.         })
  24.     }
  25. }
复制代码

到这里,微博的@功能就已经全部讲解清楚了,希望各位可以清楚明白的了解。



转自我爱猫猫技术博客

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

내 HTML5 웹 사이트에 오디오를 추가하는 방법은 무엇입니까? 내 HTML5 웹 사이트에 오디오를 추가하는 방법은 무엇입니까? Mar 10, 2025 pm 03:01 PM

이 기사는 & lt; audio & gt를 사용하여 HTML5에 오디오를 포함시키는 방법을 설명합니다. 형식 선택에 대한 모범 사례 (MP3, OGG Vorbis), 파일 최적화 및 재생에 대한 JavaScript 컨트롤을 포함한 요소. 다중 오디오를 사용하는 것을 강조합니다

HTML5 페이지 가시성 API를 사용하여 페이지가 표시되는시기를 감지하려면 어떻게합니까? HTML5 페이지 가시성 API를 사용하여 페이지가 표시되는시기를 감지하려면 어떻게합니까? Mar 13, 2025 pm 07:51 PM

이 기사에서는 HTML5 페이지 가시성 API를 사용하여 페이지 가시성을 감지하고 사용자 경험을 향상 시키며 리소스 사용량을 최적화하는 것에 대해 설명합니다. 주요 측면에는 미디어 일시 정지, CPU 부하 감소 및 가시성 변경에 기반한 분석 관리가 포함됩니다.

뷰포트 메타 태그를 사용하여 모바일 장치에서 페이지 스케일링을 제어하려면 어떻게합니까? 뷰포트 메타 태그를 사용하여 모바일 장치에서 페이지 스케일링을 제어하려면 어떻게합니까? Mar 13, 2025 pm 08:00 PM

이 기사에서는 Viewport Meta 태그를 사용하여 모바일 장치의 페이지 스케일링을 제어하여 폭과 최적의 응답 성 및 성능을위한 초기 스케일과 같은 설정에 중점을 둡니다. character count : 159

사용자 입력에 HTML5 양식을 사용하는 방법은 무엇입니까? 사용자 입력에 HTML5 양식을 사용하는 방법은 무엇입니까? Mar 10, 2025 pm 02:59 PM

이 기사에서는 HTML5 양식을 작성하고 검증하는 방법을 설명합니다. 그것은 & lt; form & gt; 요소, 입력 유형 (텍스트, 이메일, 번호 등) 및 속성 (필수, 패턴, 최소, 최대). HTML5의 장점은 오래된 방법에 비해 형성됩니다

Geolocation API로 사용자 위치 개인 정보 및 권한을 어떻게 처리합니까? Geolocation API로 사용자 위치 개인 정보 및 권한을 어떻게 처리합니까? Mar 18, 2025 pm 02:16 PM

이 기사는 지리적 위치 API를 사용하여 사용자 위치 개인 정보 및 권한 관리, 권한 요청, 데이터 보안 보장 및 개인 정보 보호법 준수에 대한 모범 사례를 강조하는 것에 대해 설명합니다.

HTML5 및 JavaScript로 대화 형 게임을 만드는 방법은 무엇입니까? HTML5 및 JavaScript로 대화 형 게임을 만드는 방법은 무엇입니까? Mar 10, 2025 pm 06:34 PM

이 기사는 JavaScript를 사용하여 대화식 HTML5 게임을 만드는 자세한 내용입니다. 게임 디자인, HTML 구조, CSS 스타일, JavaScript 로직 (이벤트 처리 및 애니메이션 포함) 및 오디오 통합을 다룹니다. 필수 JavaScript 라이브러리 (Phaser, PI

대화식 사용자 인터페이스에 HTML5 드래그 앤 드롭 API를 어떻게 사용합니까? 대화식 사용자 인터페이스에 HTML5 드래그 앤 드롭 API를 어떻게 사용합니까? Mar 18, 2025 pm 02:17 PM

이 기사는 HTML5 드래그 앤 드롭 API를 사용하여 대화식 사용자 인터페이스를 생성하고 요소를 드래그 가능하게 만들고 주요 이벤트를 처리하며 사용자 정의 피드백으로 사용자 경험을 향상시키는 방법을 자세히 설명합니다. 또한 일반적인 함정에 대해 설명합니다

클라이언트와 서버 간의 양방향 통신에 HTML5 WebSockets API를 어떻게 사용합니까? 클라이언트와 서버 간의 양방향 통신에 HTML5 WebSockets API를 어떻게 사용합니까? Mar 12, 2025 pm 03:20 PM

이 기사에서는 실시간, 양방향 클라이언트 서버 커뮤니케이션을위한 HTML5 WebSockets API를 설명합니다. 클라이언트 측 (JavaScript) 및 서버 측 (Python/Flask) 구현에 대해 자세히 설명하여 확장 성, 상태 관리,

See all articles