光标_input输入框的光标操作,以及选中方式-textRange.select()方法讲解
2017-01-01 18:30

  对于textRange,selection对象平时使用较少,而且文档介绍比较晦涩,令人望而却步。所以我觉得学习range对象的功能,最好以实际应用为例子,逐步学其功能。range是一个区域范围对象,可以操作用户选区,就是用户用鼠标选中的内容,也可以操作光标的位置。这些东西我刚开始接触的时候,从网上翻了不少文档,也看过一些大神的博客的讲解,总感觉,他们的讲解介绍要么是纯粹的API罗列,或者就是空中楼阁式的讲解,确实也有一些不错的讲解,张鑫旭的博文中对range和selection的讲解就比较亲民易懂。重新创造轮子确实也没有什么价值。我是从实用的角度,实现实际需求的功能为目的来从小白级别的探讨range对象。

    今天我们就以实现文本框中的光标位置设置为目标。在账号密码的输入中,如果输入的账号密码错误,我们为了提高用户体验,希望让错误的地方focus获取焦点。而我们只调用focus()方法的话,input获取焦点但是光标是在最前面,这时如果用户要删除内容,还需要点击让光标移动到文本的末尾。为了让用户省去这步操作就需要在focus事件中把光标移动到最后面。

    focus_before.png

上图是直接focus的结果图。

要操作光标的位置我们都有耳闻,textRange对象,没错,你答对了一半。因为textRange是IE私有对象,尽管Opera浏览器也有支持,但毕竟是模仿秀,不真着。

那么我们怎么获取textRange对象呢?查看IE的DHTML文档。

ie_creatTextRange.png

从文档中我们得到了creatTextRange方法可以创建textRange对象。

注:有一些问题我们可以百度或者google获得答案,但那只是获取的一种解决方法,你若想彻底理解某个东西,深入掌握就要从最基础的地方开始找答案,我们百度出来知道input有这么个方法,你就应该想,这些人是怎么知道有这个方法的,为了解决这个疑惑,你就需要去看IE的文档,文档上有这个东西,原来是从文档上知道的。得鱼不如得渔,授人以鱼不如授人以渔。所以我觉得学习方法很重要,即使是有老师,老师也只能教你一些基础入门的。这就是师傅领进门,修行在个人。掌握了学习方法,你就能自我进步,自我升华。我的大部分文章都是以探讨实现的方式写的,而我学习只是也正是通过这种方式来学习的。有时我介绍的知识点并不是很精彩,但是重要的是学习研究的方式你是否从中体会到了。我步入前端这行也才两年多时间,并且大部分东西都是自学的,包括写js插件,也是一步步提升的,现是模仿别人的,后来慢慢寻求那种写法更好。目前水平有限,能力一般,但两年时间一边工作,一边自我学习,对前端所涉及的东西也掌握的八九不离十了。除了一些大型的插件,像Ueditor,Echarts没有尝试过,其他的页面中用到过的效果,功能相关的插件,几乎都自己写过了。对于此,不敢多满足,但还是比较欣慰的。我的文章将一直保持研究的方式,在写一些技巧,知识点的同时,也渗入学习方法。

    扯远了。言归正传。

<input type="text" id="itext" value="mooshine"/>
var itext=document.getElementById("itext");
var rg=itext.createTextRange();

一行代码,我们得到了textRange对象。那么怎么对textRange对象进行操作呢?还得看IE的文档介绍

IE_rangeWOrd.png

通过文档api的筛选,我们看到,红框中的几个方法对我们有用。这里,我提一下select方法,select方法文档上的翻译是“将当前选中区置为当前对象”。这句话怎么理解呢?

 这句话是说,我们通过createTextRange方法创建了textRange对象,注意是创建,也就是说原本不存在这个对象。然后我们使用这个对象的collapse或者move,moveStart方法的时候,操作的都是textRange对象,而最后的状态表现是在input对象上的,select方法的作用就是,把textRange对象上的操作影印到input对象上的文本区域中。

  明白了select方法,我们看collapse方法:将插入点移动到当前范围的开始或者结束。

collapse.png

有点英语水平的,上面的介绍应该能看懂,就是说我们要想让光标移动到末尾话应该传入false,

那么使用collapse的方法代码应该是(我们定义是在itext上的onfocus事件)

itext.onfocus=focushandler;
function focushandler(){
    var rg=this.createTextRange();
    rg.collapse(false);
    rg.select();
}

用collapse可以实现光标的定位,上面还有个move方法:折叠给定文本范围并将空范围移动给定的单位数,textRange代表的是一个文本的选中范围区域,那么折叠给定范围就是开始和结束点是相同的,没有选中,只有一个光标而已。

move.png

再看move的方法介绍,两个参数,第一个是单位,第二个数值,我们得到text.value.length都是以文本character字符数计算的,其他的单位都不太好算,所以用‘character’作为计算,默认获取焦点的时候,光标是在最前面的,所以我们要往后移动length的长度,传正值就可以。

function focushandler(){
    var rg=this.createTextRange();
    var len =this.value.length;
    rg.move('character',len);
    rg.select();
}

这种方式也可以实现我们的将光标定位到文末的需求。那么我们看一下moveEnd方法能否实现我们的需求,moveEnd方法的参数同move方法。

他的解释是:更改范围的结束位置。

//不能达到我们的目的
function focushandler(){
   var rg=this.createTextRange();
   var len =this.value.length;
   rg.moveEnd('character',len);
   rg.select();
}

代码如上,运行结果如下:

moveEnd.png

moveEnd方法并没有实现我们的需求,moveEnd方法是把范围的结束移动到了末尾,但是范围的开始我们没有操作,还在开始呢,所以textRange的范围就是从文本开始到文本末尾全选中了。如此说来,我们使用moveStart应该能达到我们的需求:移动范围的开始位置。

function focushandler(){
    var rg=this.createTextRange();
    var len =this.value.length;
    rg.moveStart('character',len);
    rg.select();
}

moveStart方法是能达到我们的需求的。那么你如果动脑筋的话,会联想的moveEnd移动结束位置到了末尾,那么我们用collapse合并不就也能达到目的了。没错,这样确实能满足我们的需求,但是我们直接用collapse就能满足需求,干嘛还多余用moveEnd方法呢。

   到这里textRange操作光标的方法我们探讨完了,但是它只是IE下的方法,其他浏览器怎么办呢?

   下面是从MDN mozilla上查到的资料:

setSelection.png

从资料上我们知道其他浏览器有setSelectionRange方法能改变selection的范围,那么我们就可以实现光标的需求了。

var len=th.value.length;
th.setSelectionRange(len,len);

上面两行代码就是实现光标定位到文末的作用,但是在实际测试中是不起作用的。而我们要通过setTimeout才能起作用。

setTimeout(function(){
    console.log("selectionrange")
    var len=th.value.length;
    th.setSelectionRange(len,len);
},20);

篇幅已经不短了,这个我就不做探讨了,直接说结论:

上述代码网上也有,但大部分setTimeout的时间都是0,而这里我写成20,是因为0的话确实在火狐下是正常的,但是在chrome下不能达到目的。因为改变光标的事件有mousedown,mousemove,鼠标按下地方就是光标所在的位置,mousemove时会改变selection的选区。

经测试,火狐下在input中出现的事件顺序是:mousedown-focus-click,其实这个setTimeout的作用也并非是为火狐而做的,没有setTimeout,火狐依然能达到光标设置到文末的目的。setTimeout是为chrome的兼容出现的。在chrome下点击input时事件的顺序是mousedown-focus-mousemove-click.因为尽管你鼠标没有移动,chrome还是给执行了mousemove事件,同样也会执行mousemove的浏览器默认行为,focus中将光标移动到文末了,mousemove又将光标移动到点击的位置了。所以要用setTimeout延迟来执行,这里我为什么要用20ms,而不是0毫秒呢?这个也是测试的结论,chrome的mousemove事件本来就比focus事件延迟一步,而setTimeout设置为0的话,也是异步延迟,所以两个谁先执行,谁后执行,跟多种情况有关,这就会出现,有时好使,有时不好使的现象。所以要用20ms来延迟,这样的话,两个异步执行时,mousemove是立即执行,而setTimeout在后几个轮询中,肯定比mousemove执行的晚,这样就能达到我们的目的。理论上chrome的setTimeout的最小轮询时间是4ms,我们设置4ms就可以达到我们的目的,但实际不行,实际中chrome的mousmove添加到队列中根据电脑总体运行情况的,老机子可能会更慢。所以采用20ms来focus一定在mousemove事件后面执行。如果还是不行的话,你可以把时间再调大点,到50ms。

 最后我们把兼容代码列一下:

//通过是否支持createTextRange判断是否是IE
if(this.createTextRange){
    var rg=this.createTextRange();
    rg.collapse(false);
    rg.select();
}else{
    var th=this;
//这里注意一定要定义this为一个变量
//setTimeout才能从变量作用域中得到这个对象
//否则其延迟是由window调用的,this指向的是globle对象。
    setTimeout(function(){
        var len=th.value.length;
        th.setSelectionRange(len,len);
    },20);
}

上述代码中的IE部门,你可以根据喜好采用我们上面所讲述的集中方法,触类旁通,依次,你可以实现IE11-下的placeholder模拟,获取焦点的时候让光标定位在最前面。不多说了,自己发挥吧!

原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(7603)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1