jQuery Đặt vị trí con trỏ trong vùng văn bản


435

Làm cách nào để bạn đặt vị trí con trỏ trong trường văn bản bằng jQuery? Tôi đã có một trường văn bản có nội dung và tôi muốn con trỏ người dùng được định vị ở một độ lệch nhất định khi họ tập trung vào trường. Mã sẽ trông giống như thế này:

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

Việc triển khai hàm setC bổngPocation đó sẽ như thế nào? Nếu bạn có một trường văn bản với nội dung abcdefg, cuộc gọi này sẽ dẫn đến việc con trỏ được định vị như sau: abcd ** | ** efg.

Java có chức năng tương tự, setCaretPocation. Có một phương pháp tương tự tồn tại cho javascript?

Cập nhật: Tôi đã sửa đổi mã CMS để hoạt động với jQuery như sau:

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).setSelectionRange)? Bạn có biết điều đó giống hệt như this.setSelectionRange, chỉ chậm hơn và khó đọc hơn không? jQuery không làm gì theo nghĩa đen cho bạn ở đây.
bobince

2
Để thêm vào bình luận @bobince, hàm nên lặp lại cho từng thành phần được chọn và trả về giá trị này. Mã chính xác là trong câu trả lời của tôi.
HRJ

21
@bobince thực sự cũng không hoàn toàn chính xác. 'Đây' không phải là nút DOM, mà là đối tượng jQuery. Vì vậy, $ (this) .get (0) .setSelectionRange giống như this.get (0) .setSelectionRange, không giống như this.setSelectionRange.
Prestaul

$ (này) [0] nhanh hơn $ (này) .get (0)
EminezArtus

Kiểm tra hướng dẫn này cho giải pháp hoàn chỉnh. webdesignpluscode.blogspot.com/2017/05/ từ
Waqas Ali

Câu trả lời:


254

Tôi có hai chức năng:

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

Sau đó, bạn có thể sử dụng setCaretToPos như thế này:

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

Ví dụ trực tiếp với cả a textareavà an input, hiển thị việc sử dụng từ jQuery:

Kể từ năm 2016, đã thử nghiệm và hoạt động trên Chrome, Firefox, IE11, thậm chí IE8 (xem phần cuối cùng ở đây ; Stack Snippets không hỗ trợ IE8).


3
Tại sao sự sụp đổ (đúng) là cần thiết vì bạn sẽ thiết lập kết thúc và bắt đầu lựa chọn bù đắp?
Alexis Wilke 31/12/13

@mareoraft: Hoạt động trên textarea(và input) đối với tôi trên Chrome, Firefox, IE8 và IE11.
TJ Crowder

Tôi dường như không thể làm cho điều này hoạt động với kịch bản của mình. Tôi có một vùng văn bản trống khi tải trang, sau đó được điền bởi javascript khi ứng dụng được sử dụng. Tôi muốn dấu mũ được trả về 0 trước mỗi lần ghi mới (bản ghi về cách sử dụng). đó có phải là dữ liệu động gây ra vấn đề cho tôi không? Nếu vậy làm thế nào tôi có thể nhận được xung quanh đó?
Chris

Tầm quan trọng của chuỗi 'ký tự' là gì? Có phải chuỗi cụ thể cần phải được sử dụng?
Jon Schneider

299

Đây là một giải pháp 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();
        }
    });
};

Với điều này, bạn có thể làm

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

2
@Jlie: Làm thế nào điều đó xảy ra, tôi thường sử dụng 4. Đã sửa.
mở

1
@UberNeet: Cập nhật dựa trên đề xuất của bạn.
mở

1
@Enve: Tôi không có bản sao IE 5.5 để kiểm tra, nhưng điều đó có thể là do jQuery không hỗ trợ IE 5.5 .
mở

1
@ JaroslavZáruba: Vâng. Nó là. Nhưng cho phép bạn không phải viết selectRange($('.my_input')[0], 3, 5)nếu bạn đang sử dụng jQuery. Hơn nữa, nó nên hoạt động với nhiều hơn một yếu tố, nếu bạn cần điều đó, vì bất kỳ lý do gì. Nếu bạn muốn bản địa thuần túy, vui lòng sử dụng giải pháp của CMS.
mpen

2
Tôi cần phải thêm $('#elem').focus()trước để con trỏ nhấp nháy xuất hiện.
mareoraft

37

Các giải pháp ở đây là đúng ngoại trừ mã mở rộng jQuery.

Hàm mở rộng sẽ lặp lại qua từng phần tử được chọn và quay lại thishỗ trợ chuỗi. Dưới đây là những một phiên bản chính xác:

$.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
Mỗi hàm trả về đối tượng jquery. vì vậy bạn thực sự có thể làm: return this.each(function...)và loại bỏ dòng độc lập.
jhummel

23

Tôi tìm thấy một giải pháp hiệu quả cho tôi:

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

Bây giờ bạn có thể di chuyển tiêu điểm đến cuối của bất kỳ yếu tố nào bằng cách gọi:

$(element).focusEnd();

Hoặc bạn chỉ định vị trí.

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

3
Đối với các phần tử textarea, một cải tiến cho tiêu điểm là thêm this.scrollTop(this[0].scrollHeight);, để đảm bảo rằng textarea được cuộn để hiển thị điểm chèn.
vẽ

12

Điều này làm việc với tôi trên Safari 5 trên Mac OSX, jQuery 1.4:

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

Đối với tôi điều đó không hoạt động tốt với truy cập trực tiếp, nhưng điều này hoạt động hoàn hảo. $ (myID) .prop ('selectStart', vị trí); $ (myID) .prop ('selectEnd', vị trí);
amdan

9

Tôi nhận ra rằng đây là một bài viết rất cũ, nhưng tôi nghĩ rằng tôi nên cung cấp một giải pháp đơn giản hơn để cập nhật nó chỉ bằng 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;
}

Cách sử dụng để sử dụng ctrl-enter để thêm một dòng mới (như trong 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);
    }
});

Tôi cởi mở để phê bình. Cảm ơn bạn.

CẬP NHẬT: Giải pháp này không cho phép chức năng sao chép và dán bình thường hoạt động (ví dụ: ctrl-c, ctrl-v), vì vậy tôi sẽ phải chỉnh sửa phần này trong tương lai để đảm bảo rằng phần đó hoạt động trở lại. Nếu bạn có một ý tưởng làm thế nào để làm điều đó, xin vui lòng bình luận ở đây, và tôi sẽ rất vui khi thử nghiệm nó. Cảm ơn.


7

Trong IE để di chuyển con trỏ trên một số vị trí, mã này là đủ:

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


7

Đặt tiêu điểm trước khi bạn đã chèn văn bản vào textarea như vậy?

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

6

Điều này làm việc cho tôi trong chrome

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

Rõ ràng bạn cần độ trễ từ một phần triệu giây trở lên, vì thông thường người dùng tập trung vào trường văn bản bằng cách nhấp vào một số vị trí trong trường văn bản (hoặc bằng cách nhấn tab) mà bạn muốn ghi đè, vì vậy bạn phải đợi đến vị trí đó được đặt bởi người dùng nhấp và sau đó thay đổi nó.


Không được phép gán các thuộc tính chỉ đọc trong chế độ nghiêm ngặt
Ivan Rubinson

4

Chỉ cần nhớ trả về false ngay sau khi gọi hàm nếu bạn đang sử dụng các phím mũi tên vì Chrome sẽ tạo ra frack khác.

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

2
Đó không phải là thực hành tốt nhất để return false;. Bạn muốn event.preventDefault();thay thế. Nếu bạn trả lại sai, bạn đang ám chỉ event.stopPropagation()điều không phải lúc nào cũng mong muốn
Alan H.

4

Dựa trên câu hỏi này , câu trả lời sẽ không hoạt động hoàn hảo cho tức là và opera khi có dòng mới trong textarea. Câu trả lời giải thích cách điều chỉnh selectStart, selectEnd trước khi gọi setSelectionRange.

Tôi đã thử điều chỉnh Offerset từ câu hỏi khác với giải pháp được đề xuất bởi @AVProgrammer và nó hoạt động.

function adjustOffset(el, offset) {
    /* From https://stackoverflow.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://stackoverflow.com/a/7180862/611741 */
    if(this.lengh == 0) return this;
    return $(this).setSelection(position, position);
}

$.fn.setSelection = function(selectionStart, selectionEnd) {
    /* From https://stackoverflow.com/a/7180862/611741 
       modified to fit https://stackoverflow.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://stackoverflow.com/a/7180862/611741 */
    this.setCursorPosition(this.val().length);
}

4

Sửa đổi nhỏ cho mã tôi tìm thấy trong bitbucket

Mã bây giờ có thể chọn / tô sáng với điểm bắt đầu / kết thúc nếu được cho 2 vị trí. Đã thử nghiệm và hoạt động tốt trong FF / Chrome / IE9 / Opera.

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

Mã được liệt kê dưới đây, chỉ một vài dòng thay đổi:

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

Chạy trong Chrome 39, IE11, Safari 5.1.7 nhưng không chạy trong Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins

3

Tôi đã phải làm việc này cho các phần tử có thể nội dung và jQuery và nghĩ rằng ai đó có thể muốn nó sẵn sàng để sử dụng:

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

Cách sử dụng $(selector).getCaret()trả về số bù và $(selector).setCaret(num)thiết lập offset và đặt trọng tâm vào phần tử.

Cũng là một mẹo nhỏ, nếu bạn chạy $(selector).setCaret(num)từ bàn điều khiển, nó sẽ trả về console.log nhưng bạn sẽ không hình dung được trọng tâm vì nó được thiết lập ở cửa sổ giao diện điều khiển.

Tốt nhất; D


1

Bạn có thể trực tiếp thay đổi nguyên mẫu nếu setSelectionRange không tồn tại.

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

liên kết jsFiddle

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.