jQuery Mengatur Posisi Kursor di Area Teks

435

Bagaimana Anda mengatur posisi kursor dalam bidang teks menggunakan jQuery? Saya punya bidang teks dengan konten, dan saya ingin kursor pengguna diposisikan pada offset tertentu ketika mereka fokus pada bidang tersebut. Kode akan terlihat seperti ini:

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

Seperti apa penerapan fungsi setCursorPosition itu? Jika Anda memiliki bidang teks dengan konten abcdefg, panggilan ini akan mengakibatkan kursor diposisikan sebagai berikut: abcd ** | ** efg.

Java memiliki fungsi serupa, setCaretPosition. Apakah ada metode serupa untuk javascript?

Pembaruan: Saya memodifikasi kode CMS agar berfungsi dengan jQuery sebagai berikut:

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);
jcnnghm
sumber
78
$(this).get(0).setSelectionRange)? Anda tahu itu persis sama this.setSelectionRange, hanya lebih lambat dan lebih sulit untuk dibaca, bukan? jQuery benar-benar tidak melakukan apa pun untuk Anda di sini.
bobince
2
Untuk menambahkan komentar @obobince, fungsi harus beralih untuk setiap elemen yang dipilih dan mengembalikannya. Kode yang benar ada dalam jawaban saya.
HRJ
21
@obobince sebenarnya juga tidak sepenuhnya benar. 'ini' bukan node DOM, tetapi objek jQuery. Jadi, $ (this) .get (0) .setSelectionRange sama dengan this.get (0) .setSelectionRange, tidak sama dengan this.setSelectionRange.
Prestaul
$ (ini) [0] lebih cepat dari $ (ini) .get (0)
EminezArtus
Lihatlah tutorial ini untuk solusi lengkap. webdesignpluscode.blogspot.com/2017/05/…
Waqas Ali

Jawaban:

254

Saya memiliki dua fungsi:

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

Kemudian Anda dapat menggunakan setCaretToPos seperti ini:

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

Contoh langsung dengan a textareadan a input, menunjukkan penggunaan dari jQuery:

Pada 2016, diuji dan bekerja di Chrome, Firefox, IE11, bahkan IE8 (lihat yang terakhir di sini ; Cuplikan Stack tidak mendukung IE8).

CMS
sumber
3
Mengapa runtuh (benar) diperlukan karena Anda akan mengatur akhir dan memulai offset seleksi?
Alexis Wilke
@mareoraft: Bekerja pada textarea(dan input) untuk saya di Chrome, Firefox, IE8, dan IE11.
TJ Crowder
Saya sepertinya tidak bisa melakukan ini dengan skrip saya. Saya memiliki area teks yang kosong pada pemuatan halaman, kemudian diisi oleh javascript saat aplikasi digunakan. Saya ingin tanda sisipan dikembalikan ke 0 sebelum setiap penulisan baru (catatan penggunaan). apakah data dinamis yang menyebabkan masalah bagi saya? jika demikian bagaimana saya bisa mengatasi itu?
Chris
Apa pentingnya 'karakter' string literal? Apakah string spesifik itu perlu digunakan?
Jon Schneider
299

Inilah solusi 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();
        }
    });
};

Dengan ini, Anda bisa melakukannya

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
Mpen
sumber
2
@ Jesse: Entah bagaimana itu terjadi, saya biasanya menggunakan 4. Tetap.
mpen
1
@UberNeet: Diperbarui berdasarkan saran Anda.
mpen
1
@ Ennve: Saya tidak punya salinan IE 5.5 untuk diuji, tapi itu mungkin karena jQuery tidak mendukung IE 5.5 .
mpen
1
@ JaroslavZáruba: Ya. Ini. Tetapi memungkinkan Anda untuk tidak harus menulis selectRange($('.my_input')[0], 3, 5)jika Anda sudah menggunakan jQuery. Lebih lanjut, itu harus bekerja dengan lebih dari satu elemen, jika Anda membutuhkannya, untuk alasan apa pun. Jika Anda ingin yang asli murni, silakan gunakan solusi CMS.
mpen
2
Saya perlu menambahkan $('#elem').focus()terlebih dahulu untuk mendapatkan kursor berkedip muncul.
mareoraft
37

Solusi di sini benar kecuali untuk kode ekstensi jQuery.

Fungsi ekstensi harus beralih pada setiap elemen yang dipilih dan kembali thiske rantai dukungan. Berikut ini yang merupakan versi yang benar:

$.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;
};
HRJ
sumber
4
Setiap fungsi mengembalikan objek jquery. sehingga Anda benar-benar dapat melakukannya: return this.each(function...)dan menghapus garis mandiri.
jhummel
23

Saya menemukan solusi yang berfungsi untuk saya:

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

Sekarang Anda dapat memindahkan fokus ke akhir elemen apa pun dengan menelepon:

$(element).focusEnd();

Atau Anda menentukan posisi.

$(element).setCursorPosition(3); // This will focus on the third character.
Program AV
sumber
3
Untuk elemen textarea, perbaikan pada focusEnd adalah menambahkan this.scrollTop(this[0].scrollHeight);, untuk memastikan bahwa textarea digulir untuk membuat titik penyisipan terlihat.
Drew
12

Ini bekerja untuk saya di Safari 5 di Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
BobFromBris
sumber
bagi saya itu tidak berfungsi dengan baik dengan akses langsung, tetapi ini bekerja dengan sempurna. $ (myID) .prop ('selectionStart', position); $ (myID) .prop ('selectionEnd', position);
amdan
9

Saya menyadari bahwa ini adalah posting yang sangat lama, tetapi saya berpikir bahwa saya mungkin harus menawarkan solusi yang lebih sederhana untuk memperbaruinya hanya menggunakan 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;
}

Penggunaan untuk menggunakan ctrl-enter untuk menambahkan baris baru (seperti di 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);
    }
});

Saya terbuka untuk kritik. Terima kasih.

PEMBARUAN: Solusi ini tidak memungkinkan fungsionalitas salin dan tempel normal berfungsi (yaitu ctrl-c, ctrl-v), jadi saya harus mengedit ini di masa mendatang untuk memastikan bagian itu berfungsi lagi. Jika Anda memiliki ide bagaimana melakukannya, silakan komentar di sini, dan saya akan dengan senang hati mengujinya. Terima kasih.

tofirius
sumber
7

Di IE untuk memindahkan kursor pada beberapa posisi, kode ini cukup:

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

sumber
7

Tetapkan fokus sebelum Anda memasukkan teks ke dalam textarea demikian?

$("#comments").focus();
$("#comments").val(comments);
Steven Whitby
sumber
6

Ini bekerja untuk saya di chrome

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

Rupanya Anda memerlukan penundaan mikrodetik atau lebih, karena biasanya pengguna fokus pada bidang teks dengan mengklik pada beberapa posisi di bidang teks (atau dengan menekan tab) yang ingin Anda timpa, jadi Anda harus menunggu sampai posisi tersebut diatur oleh pengguna klik lalu ubah.

Hung Tran
sumber
Penugasan ke properti hanya-baca tidak diperbolehkan dalam mode ketat
Ivan Rubinson
4

Ingatlah untuk mengembalikan false tepat setelah pemanggilan fungsi jika Anda menggunakan tombol panah karena Chrome membelah frack up sebaliknya.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
erich
sumber
2
Ini bukan praktik terbaik return false;. Anda event.preventDefault();malah mau . Jika Anda mengembalikan false, Anda menyiratkan event.stopPropagation()yang tidak selalu diinginkan
Alan H.
4

Berdasarkan pertanyaan ini , jawabannya tidak akan bekerja dengan sempurna untuk ie dan opera ketika ada baris baru di textarea. The jawaban menjelaskan bagaimana untuk menyesuaikan selectionStart, selectionEnd sebelum menelepon setSelectionRange.

Saya telah mencoba AdjustOffset dari pertanyaan lain dengan solusi yang diajukan oleh @AVProgrammer dan berfungsi.

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);
}
Ghislain Hivon
sumber
4

Modifikasi kecil pada kode yang saya temukan di bitbucket

Kode sekarang dapat memilih / menyorot dengan titik awal / akhir jika diberi 2 posisi. Diuji dan berfungsi dengan baik di FF / Chrome / IE9 / Opera.

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

Kode tercantum di bawah ini, hanya beberapa baris yang berubah:

(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)
ranting
sumber
Berjalan di Chrome 39, IE11, Safari 5.1.7 tetapi tidak di Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins
3

Saya harus membuatnya berfungsi untuk elemen yang dapat diedit dan jQuery dan seseorang mungkin ingin itu siap digunakan:

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

Penggunaan $(selector).getCaret()mengembalikan offset angka dan $(selector).setCaret(num)menetapkan offset dan menetapkan fokus pada elemen.

Juga tip kecil, jika Anda menjalankan $(selector).setCaret(num)dari konsol itu akan mengembalikan konsol.log tetapi Anda tidak akan memvisualisasikan fokus karena didirikan di jendela konsol.

Bests; D

FGZ
sumber
1

Anda dapat langsung mengubah prototipe jika setSelectionRange tidak ada.

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

tautan jsFiddle

Anoop
sumber