jQuery在文本区域中设置光标位置

Posted

技术标签:

【中文标题】jQuery在文本区域中设置光标位置【英文标题】:jQuery Set Cursor Position in Text Area 【发布时间】:2010-10-04 16:32:30 【问题描述】:

如何使用 jQuery 在文本字段中设置光标位置?我有一个包含内容的文本字段,我希望用户光标在他们关注该字段时定位在某个偏移量处。代码应该是这样的:

$('#input').focus(function() 
  $(this).setCursorPosition(4);
);

setCursorPosition 函数的实现是什么样的?如果您有一个内容为 abcdefg 的文本字段,则此调用将导致光标定位如下:abcd**|**efg。

Java 有一个类似的函数,setCaretPosition。 javascript是否存在类似的方法?

更新:我修改了 CMS 的代码以使用 jQuery,如下所示:

new function($) 
  $.fn.setCursorPosition = function(pos) 
    if (this.setSelectionRange) 
      this.setSelectionRange(pos, pos);
     else if (this.createTextRange) 
      var range = this.createTextRange();
      range.collapse(true);
      if(pos < 0) 
        pos = $(this).val().length + pos;
      
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    
  
(jQuery);

【问题讨论】:

$(this).get(0).setSelectionRange)?你知道这和this.setSelectionRange 完全一样,只是更慢更难阅读,对吧? jQuery 在这里对你没有任何帮助。 要添加到@bobince 注释,函数应该迭代每个选定的元素并返回它。正确的代码在我的答案中。 @bobince 实际上也不完全正确。 'this' 不是 DOM 节点,而是 jQuery 对象。所以,$(this).get(0).setSelectionRange 与 this.get(0).setSelectionRange 相同,与 this.setSelectionRange 不同。 $(this)[0] 比 $(this).get(0) 快 查看本教程以获得完整的解决方案。 webdesignpluscode.blogspot.com/2017/05/… 【参考方案1】:

我有两个功能:

function setSelectionRange(input, selectionStart, selectionEnd) 
  if (input.setSelectionRange) 
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  
  else if (input.createTextRange) 
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  


function setCaretToPos (input, pos) 
  setSelectionRange(input, pos, pos);

然后你可以像这样使用 setCaretToPos:

setCaretToPos(document.getElementById("YOURINPUT"), 4);

textareainput 的实时示例,展示了 jQuery 的用法:

function setSelectionRange(input, selectionStart, selectionEnd) 
  if (input.setSelectionRange) 
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
   else if (input.createTextRange) 
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  


function setCaretToPos(input, pos) 
  setSelectionRange(input, pos, pos);


$("#set-textarea").click(function() 
  setCaretToPos($("#the-textarea")[0], 10)
);
$("#set-input").click(function() 
  setCaretToPos($("#the-input")[0], 10);
);
<textarea id="the-textarea" cols="40" rows="4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</textarea>
<br><input type="button" id="set-textarea" value="Set in textarea">
<br><input id="the-input" type="text" size="40" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit">
<br><input type="button" id="set-input" value="Set in input">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

截至 2016 年,已在 Chrome、Firefox、IE11 甚至 IE8 上进行了测试和工作(请参阅最后一个 here;堆栈片段不支持 IE8)。

【讨论】:

既然要设置结束和开始选择的偏移量,为什么需要 collapse(true)? @mareoraft:在 Chrome、Firefox、IE8 和 IE11 上为我使用 textarea(和 input)。 我似乎无法让它与我的脚本一起使用。我有一个在页面加载时为空的文本区域,然后在使用应用程序时由 javascript 填充。我希望在每次新写入(使用记录)之前将插入符号返回到 0。是动态数据导致我出现问题吗?如果是这样,我该如何解决? 字符串文字'character'的意义是什么?是否需要使用该特定字符串? 根据 createTextRange 应该避免。 developer.mozilla.org/en-US/docs/Web/API/TextRange【参考方案2】:

这是一个 jQuery 解决方案:

$.fn.selectRange = function(start, end) 
    if(end === undefined) 
        end = start;
    
    return this.each(function() 
        if('selectionStart' in this) 
            this.selectionStart = start;
            this.selectionEnd = end;
         else if(this.setSelectionRange) 
            this.setSelectionRange(start, end);
         else if(this.createTextRange) 
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        
    );
;

有了这个,你可以做到

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
JsFiddle JsBin

【讨论】:

@Jesse:不知道这是怎么回事,我通常使用 4。已修复。 @UberNeet:根据您的建议更新。 @Enve:我没有 IE 5.5 的副本可供测试,但这可能是因为jQuery doesn't support IE 5.5。 @JaroslavZáruba:是的。它是。但是,如果您已经在使用 jQuery,则允许您不必 编写 selectRange($('.my_input')[0], 3, 5)。此外,无论出于何种原因,如果您需要,它应该与多个元素一起使用。如果你想要纯原生,请使用CMS的解决方案。 我需要事先添加$('#elem').focus() 才能让闪烁的光标出现。【参考方案3】:

在 IE 中将光标移动到某个位置,这段代码就足够了:

var range = elt.createTextRange();
range.move('character', pos);
range.select();

【讨论】:

【参考方案4】:

我正在使用这个:http://plugins.jquery.com/project/jCaret

【讨论】:

【参考方案5】:

这在 Mac OSX、jQuery 1.4 上的 Safari 5 上对我有用:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;

【讨论】:

对我来说,直接访问无法正常工作,但效果很好。 $(myID).prop('selectionStart', 位置); $(myID).prop('selectionEnd', position);【参考方案6】:

这里的解决方案都是对的,除了jQuery扩展代码。

扩展函数应该遍历每个选定的元素并返回this 以支持链接。这是 a 正确版本:

$.fn.setCursorPosition = function(pos) 
  this.each(function(index, elem) 
    if (elem.setSelectionRange) 
      elem.setSelectionRange(pos, pos);
     else if (elem.createTextRange) 
      var range = elem.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    
  );
  return this;
;

【讨论】:

每个函数都返回jquery对象。所以你实际上可以这样做:return this.each(function...) 并删除独立行。【参考方案7】:

如果您使用箭头键,请记住在函数调用后立即返回 false,否则 Chrome 会搞砸。


    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;

【讨论】:

return false; 不是最佳做法。你想要event.preventDefault();。如果您返回 false,则暗示 event.stopPropagation() 这并不总是可取的【参考方案8】:

我找到了适合我的解决方案:

$.fn.setCursorPosition = function(position)
    if(this.length == 0) return this;
    return $(this).setSelection(position, position);


$.fn.setSelection = function(selectionStart, selectionEnd) 
    if(this.length == 0) return this;
    var input = this[0];

    if (input.createTextRange) 
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
     else if (input.setSelectionRange) 
        input.focus();
        input.setSelectionRange(selectionStart, selectionEnd);
    

    return this;


$.fn.focusEnd = function()
    this.setCursorPosition(this.val().length);
            return this;

现在您可以通过调用将焦点移到任何元素的末尾:

$(element).focusEnd();

或者你指定位置。

$(element).setCursorPosition(3); // This will focus on the third character.

【讨论】:

对于textarea元素,对focusEnd的改进是添加this.scrollTop(this[0].scrollHeight);,保证textarea滚动显示插入点。 根据 Mozilla,我会直接避免使用任何使用 createTextRange - developer.mozilla.org/en-US/docs/Web/API/TextRange【参考方案9】:

如果setSelectionRange不存在,你可以直接更改原型。

(function() 
    if (!htmlInputElement.prototype.setSelectionRange) 
        HTMLInputElement.prototype.setSelectionRange = function(start, end) 
            if (this.createTextRange) 
                var range = this.createTextRange();
                this.collapse(true);
                this.moveEnd('character', end);
                this.moveStart('character', start);
                this.select();
            
        
    
)();
document.getElementById("input_tag").setSelectionRange(6, 7);

jsFiddle链接

【讨论】:

【参考方案10】:

基于此question,当文本区域中有新行时,答案对于ie 和opera 将无法完美运行。 answer 解释了如何在调用 setSelectionRange 之前调整 selectionStart、selectionEnd。

我已经用@AVProgrammer 提出的解决方案尝试了另一个问题的adjustOffset,它可以工作。

function adjustOffset(el, offset) 
    /* From https://***.com/a/8928945/611741 */
    var val = el.value, newOffset = offset;
    if (val.indexOf("\r\n") > -1) 
        var matches = val.replace(/\r\n/g, "\n").slice(0, offset).match(/\n/g);
        newOffset += matches ? matches.length : 0;
    
    return newOffset;


$.fn.setCursorPosition = function(position)
    /* From https://***.com/a/7180862/611741 */
    if(this.lengh == 0) return this;
    return $(this).setSelection(position, position);


$.fn.setSelection = function(selectionStart, selectionEnd) 
    /* From https://***.com/a/7180862/611741 
       modified to fit https://***.com/a/8928945/611741 */
    if(this.lengh == 0) return this;
    input = this[0];

    if (input.createTextRange) 
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
     else if (input.setSelectionRange) 
        input.focus();
        selectionStart = adjustOffset(input, selectionStart);
        selectionEnd = adjustOffset(input, selectionEnd);
        input.setSelectionRange(selectionStart, selectionEnd);
    

    return this;


$.fn.focusEnd = function()
    /* From https://***.com/a/7180862/611741 */
    this.setCursorPosition(this.val().length);

【讨论】:

【参考方案11】:

对我在bitbucket找到的代码进行小修改

如果给定 2 个位置,代码现在可以选择/突出显示起点/终点。 在 FF/Chrome/IE9/Opera 中测试并正常工作。

$('#field').caret(1, 9);

代码如下,只改动了几行:

(function($) 
  $.fn.caret = function(pos) 
    var target = this[0];
    if (arguments.length == 0)  //get
      if (target.selectionStart)  //DOM
        var pos = target.selectionStart;
        return pos > 0 ? pos : 0;
      
      else if (target.createTextRange)  //IE
        target.focus();
        var range = document.selection.createRange();
        if (range == null)
            return '0';
        var re = target.createTextRange();
        var rc = re.duplicate();
        re.moveToBookmark(range.getBookmark());
        rc.setEndPoint('EndToStart', re);
        return rc.text.length;
      
      else return 0;
    

    //set
    var pos_start = pos;
    var pos_end = pos;

    if (arguments.length > 1) 
        pos_end = arguments[1];
    

    if (target.setSelectionRange) //DOM
      target.setSelectionRange(pos_start, pos_end);
    else if (target.createTextRange)  //IE
      var range = target.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos_end);
      range.moveStart('character', pos_start);
      range.select();
    
  
)(jQuery)

【讨论】:

在 Chrome 39、IE11、Safari 5.1.7 中运行,但在 Firefox 34 中不运行:jsfiddle.net/0t94z82k/6【参考方案12】:

在将文本插入文本区域之前设置焦点?

$("#comments").focus();
$("#comments").val(comments);

【讨论】:

【参考方案13】:

这在 chrome 中对我有用

$('#input').focus(function() 
    setTimeout( function() 
        document.getElementById('input').selectionStart = 4;
        document.getElementById('input').selectionEnd = 4;
    , 1);
);

显然您需要一微秒或更长时间的延迟,因为通常用户通过单击您想要覆盖的文本字段中的某个位置(或通过点击标签)来关注文本字段,因此您必须等到位置由用户单击设置,然后更改。

【讨论】:

严格模式下不允许分配给只读属性【参考方案14】:

我确实意识到这是一篇非常古老的帖子,但我认为我应该提供一个更简单的解决方案来仅使用 jQuery 来更新它。

function getTextCursorPosition(ele)    
    return ele.prop("selectionStart");


function setTextCursorPosition(ele,pos) 
    ele.prop("selectionStart", pos + 1);
    ele.prop("selectionEnd", pos + 1);


function insertNewLine(text,cursorPos) 
    var firstSlice = text.slice(0,cursorPos);
    var secondSlice = text.slice(cursorPos);

    var new_text = [firstSlice,"\n",secondSlice].join('');

    return new_text;

使用 ctrl-enter 添加新行的用法(如在 Facebook 中):

$('textarea').on('keypress',function(e)
    if (e.keyCode == 13 && !e.ctrlKey) 
        e.preventDefault();
        //do something special here with just pressing Enter
    else if (e.ctrlKey)
        //If the ctrl key was pressed with the Enter key,
        //then enter a new line break into the text
        var cursorPos = getTextCursorPosition($(this));                

        $(this).val(insertNewLine($(this).val(), cursorPos));
        setTextCursorPosition($(this), cursorPos);
    
);

我愿意接受批评。谢谢。

更新:这个解决方案不允许正常的复制和粘贴功能工作(即 ctrl-c、ctrl-v),所以我将来必须编辑它以确保该部分再次工作。如果您知道如何做到这一点,请在此处发表评论,我很乐意对其进行测试。谢谢。

【讨论】:

【参考方案15】:

我必须让它适用于 contenteditable 元素和 jQuery,并且有人可能希望它准备好使用:

$.fn.getCaret = function(n) 
    var d = $(this)[0];
    var s, r;
    r = document.createRange();
    r.selectNodeContents(d);
    s = window.getSelection();
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return s.anchorOffset;
;

$.fn.setCaret = function(n) 
    var d = $(this)[0];
    d.focus();
    var r = document.createRange();
    var s = window.getSelection();
    r.setStart(d.childNodes[0], n);
    r.collapse(true);
    s.removeAllRanges();
    s.addRange(r);
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return this;
;

使用$(selector).getCaret() 返回数字偏移量,$(selector).setCaret(num) 建立偏移量并将焦点设置在元素上。

还有一个小技巧,如果您从控制台运行$(selector).setCaret(num),它将返回console.log,但您不会看到焦点,因为它是在控制台窗口中建立的。

最佳 ;D

【讨论】:

【参考方案16】:

对我来说,最简单的方法是通过光标位置在 textarea 中添加文本(Tab -> \t)并将焦点保存在光标上:

$('#text').keyup(function () 
    var cursor = $('#text').prop('selectionStart');
    //if cursot is first in textarea
    if (cursor == 0) 
        //i will add tab in line
        $('#text').val('\t' + $('#text').val());
        //here we set the cursor position
        $('#text').prop('selectionEnd', 1);
     else 
        var value = $('#text').val();
        //save the value before cursor current position
        var valToCur = value.substring(0, cursor);
        //save the value after cursor current position
        var valAfter = value.substring(cursor, value.length);
        //save the new value with added tab in text
        $('#text').val(valToCur + '\t' + valAfter);
        //set focus of cursot after insert text (1 = because I add only one symbol)
        $('#text').prop('selectionEnd', cursor + 1);
    
);

【讨论】:

以上是关于jQuery在文本区域中设置光标位置的主要内容,如果未能解决你的问题,请参考以下文章

jquery在contenteditable div中设置光标位置

在文本区域中的光标位置之后插入文本

获取文本区域中的光标位置

文本区域中的光标位置(字符索引,不是 x/y 坐标)

如何在文本区域的末尾设置光标?

如何在文本区域标签中的特定光标位置插入选择标签下拉值作为文本片段?