jQuery设置文本区域中的光标位置。

451

如何使用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);

78
你知道$(this).get(0).setSelectionRangethis.setSelectionRange完全一样,只是更慢且更难读吗?在这里jQuery实际上没有做任何事情。 - bobince
2
补充@bobince的评论,该函数应该对每个选定的元素进行迭代并返回结果。正确的代码在我的答案中。 - HRJ
21
@bobince的说法并不完全正确。'this'不是DOM节点,而是jQuery对象。因此,$(this).get(0).setSelectionRange与this.get(0).setSelectionRange相同,但与this.setSelectionRange不同。 - Prestaul
$(this)[0] 比 $(this).get(0) 更快。 - EminezArtus
请查看此教程以获取完整解决方案。http://webdesignpluscode.blogspot.com/2017/05/how-to-set-and-get-cursor-position-in.html - Waqas Ali
16个回答

302

这里是一个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

2
@Jesse:不知道怎么发生的,我通常使用4。已修复。 - mpen
1
@UberNeet:根据您的建议进行了更新。 - mpen
1
@Enve:我没有IE 5.5的副本进行测试,但这可能是因为jQuery不支持IE 5.5 - mpen
1
@Mark 我对原生JS没有任何意见。 - Jaroslav Záruba
2
我需要先添加 $('#elem').focus() 以使闪烁的光标出现。 - mareoraft
显示剩余5条评论

264

我有两个函数:

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);

使用jQuery演示了同时包含textareainput的实时示例:

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上使用(请点击此处查看;Stack Snippets不支持IE8)。


3
既然您要设置结束和开始选择偏移量,为什么还需要使用collapse(true)呢? - Alexis Wilke
@mareoraft:在Chrome,Firefox,IE8和IE11上,对于textarea(和input) 对我有效。 - T.J. Crowder
我似乎无法让它与我的脚本一起工作。我有一个文本区域,在页面加载时为空,然后随着应用程序的使用而由JavaScript填充。我希望在每次新写入(使用记录)之前将插入符号返回到0。是动态数据导致了我的问题吗?如果是,我该如何解决? - CodingInTheUK
“character”这个字符串字面量有什么重要性吗?需要使用这个特定的字符串吗? - Jon Schneider
根据文档,应避免使用createTextRange。https://developer.mozilla.org/zh-CN/docs/Web/API/TextRange - Justin

37

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

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

$.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;
};

4
each()函数返回jQuery对象,因此您实际上可以这样做:return this.each(function...),并删除独立的行。 - jhummel

24
我找到了一个适合我使用的解决方案:
$.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.

4
针对textarea元素,改进focusEnd的方法是添加this.scrollTop(this[0].scrollHeight);,以确保滚动条将插入点可见。 - Drew
根据 Mozilla 的建议,我会直接避免使用任何使用 createTextRange 的东西 - https://developer.mozilla.org/en-US/docs/Web/API/TextRange - Justin

13

这种方法在我的 Mac OSX 上的 Safari 5 和 jQuery 1.4 中有效:

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

对我而言,直接访问并不能正常工作,但这个方法完美地解决了问题。 $(myID).prop('selectionStart', position); $(myID).prop('selectionEnd', position); - gxmad

9

我知道这是一个非常古老的帖子,但我想用仅使用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),所以我将来必须编辑它,以确保该部分再次正常工作。如果您有任何想法,请在此处评论,我将很乐意测试它。谢谢。


7

7
在IE中,要将光标移动到某个位置,只需要使用以下代码即可:

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

7
在将文本插入文本区域之前,请先设置焦点,如何实现?
$("#comments").focus();
$("#comments").val(comments);

6
这对我在Chrome中有效。
$('#input').focus(function() {
    setTimeout( function() {
        document.getElementById('input').selectionStart = 4;
        document.getElementById('input').selectionEnd = 4;
    }, 1);
});

显然,您需要延迟一微秒或更长时间,因为通常用户通过在文本字段中某个位置单击(或按tab键)来聚焦于文本字段,而您希望覆盖它,因此您必须等待用户点击设置位置,然后再进行更改。


在严格模式下,不允许对只读属性进行赋值。 - Ivan Rubinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接