jQuery ตั้งค่าตำแหน่งเคอร์เซอร์ในพื้นที่ข้อความ


435

คุณจะกำหนดตำแหน่งเคอร์เซอร์ในฟิลด์ข้อความโดยใช้ jQuery ได้อย่างไร ฉันมีช่องข้อความที่มีเนื้อหาและฉันต้องการให้เคอร์เซอร์ผู้ใช้อยู่ในตำแหน่งออฟเซ็ตที่แน่นอนเมื่อพวกเขามุ่งเน้นไปที่ฟิลด์ รหัสควรมีลักษณะเช่นนี้:

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

การใช้งานฟังก์ชัน setCursorPosition นั้นมีลักษณะอย่างไร หากคุณมีช่องข้อความที่มีเนื้อหา abcdefg การโทรนี้จะส่งผลให้เคอร์เซอร์อยู่ในตำแหน่งดังต่อไปนี้: abcd ** | ** efg

Java มีฟังก์ชั่นที่คล้ายกัน setCaretPosition มีวิธีการที่คล้ายคลึงกันสำหรับจาวาสคริปต์หรือไม่?

อัปเดต: ฉันแก้ไขโค้ดของ 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).setSelectionRange)? คุณรู้หรือไม่ว่าเป็นแบบthis.setSelectionRangeเดียวกับที่อ่านช้ากว่าและอ่านยากกว่าใช่ไหม jQuery ไม่ได้ทำสิ่งใดเพื่อคุณอย่างแท้จริง
bobince

2
หากต้องการเพิ่มความคิดเห็น @bobince ฟังก์ชันควรวนซ้ำสำหรับองค์ประกอบที่เลือกแต่ละรายการและส่งคืน รหัสที่ถูกต้องอยู่ในคำตอบของฉัน
HRJ

21
@bince เป็นจริงไม่ถูกต้องเช่นกัน 'this' ไม่ใช่โหนด DOM แต่เป็นวัตถุ jQuery ดังนั้น $ (this) .get (0) .setSelectionRange เหมือนกับ this.get (0) .setSelectionRange ไม่เหมือนกับ this.setSelectionRange
Prestaul

$ (นี่) [0] เร็วขึ้นแล้ว $ (รับ) .get (0)
EminezArtus

ลองดูบทช่วยสอนนี้เพื่อดูวิธีแก้ปัญหาอย่างสมบูรณ์ webdesignpluscode.blogspot.com/2017/05/…
Waqas Ali

คำตอบ:


254

ฉันมีสองหน้าที่:

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

ตัวอย่างสดพร้อมทั้งtextareaและและinputแสดงการใช้งานจาก jQuery:

ตั้งแต่ปี 2559 มีการทดสอบและทำงานกับ Chrome, Firefox, IE11 หรือแม้แต่ IE8 (ดูที่นี่ครั้งสุดท้ายStack Snippets ไม่รองรับ IE8)


3
เหตุใดการยุบ (จริง) จึงมีความจำเป็นเนื่องจากคุณจะกำหนดจุดสิ้นสุดและเริ่มการเลือกการชดเชย
Alexis Wilke

@ mareoraft: ทำงานบนtextarea(และinput) สำหรับฉันบน Chrome, Firefox, IE8 และ IE11
TJ Crowder

ฉันดูเหมือนจะไม่สามารถทำให้เรื่องนี้ทำงานกับสคริปต์ของฉันได้ ฉันมีพื้นที่ข้อความที่ว่างเปล่าเมื่อโหลดหน้าเว็บแล้วกรอกด้วย javascript เนื่องจากมีการใช้แอปพลิเคชัน ฉันต้องการให้เครื่องหมายรูปหมวกกลับเป็น 0 ก่อนเขียนใหม่แต่ละครั้ง (บันทึกการใช้งาน) เป็นข้อมูลไดนามิกที่ทำให้เกิดปัญหาหรือไม่ ถ้าเป็นเช่นนั้นฉันจะแก้ไขได้อย่างไร
Chris

ความสำคัญของ 'ตัวอักษร' สตริงตัวอักษรคืออะไร? จำเป็นต้องใช้สตริงที่เฉพาะเจาะจงหรือไม่?
Jon Schneider

299

นี่คือโซลูชัน 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: Dunno เกิดอะไรขึ้นฉันมักจะใช้ 4. แก้ไข
mpen

1
@UberNeet: อัปเดตตามข้อเสนอแนะของคุณ
mpen

1
@Enve: ฉันไม่ได้มีสำเนาของ IE 5.5 ในการทดสอบด้วย แต่ที่มีแนวโน้มว่าจะเป็นเพราะjQuery ไม่สนับสนุน IE 5.5
mpen

1
@ JaroslavZáruba: ใช่ มันคือ. แต่อนุญาตให้คุณไม่ต้องเขียนselectRange($('.my_input')[0], 3, 5)หากคุณใช้ jQuery อยู่แล้ว นอกจากนี้ควรทำงานกับองค์ประกอบมากกว่าหนึ่งรายการหากคุณต้องการด้วยเหตุผลใดก็ตาม ถ้าคุณต้องการชาวพื้นเมืองแท้ๆโปรดใช้โซลูชันของ CMS
mpen

2
ฉันต้องเพิ่ม$('#elem').focus()ล่วงหน้าเพื่อให้เคอร์เซอร์กะพริบปรากฏขึ้น
mareoraft

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
แต่ละฟังก์ชั่นส่งกลับวัตถุ jquery เพื่อให้คุณสามารถทำได้จริง: return this.each(function...)และลบบรรทัดแบบสแตนด์อโลน
jhummel

23

ฉันพบวิธีแก้ปัญหาที่เหมาะกับฉัน:

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

3
สำหรับองค์ประกอบ textarea การปรับปรุงเพื่อ focusEnd คือการเพิ่มthis.scrollTop(this[0].scrollHeight);เพื่อให้แน่ใจว่า textarea ถูกเลื่อนเพื่อทำให้จุดแทรกมองเห็นได้
ดึง

12

สิ่งนี้ใช้ได้กับฉันใน Safari 5 บน Mac OSX, jQuery 1.4:

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

สำหรับฉันที่ไม่ทำงานได้ดีกับการเข้าถึงโดยตรง แต่มันทำงานได้อย่างสมบูรณ์ $ (myID) .prop ('selectionStart', position); $ (myID) .prop ('selectionEnd', ตำแหน่ง);
amdan

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

ใน IE เพื่อเลื่อนเคอร์เซอร์ไปที่ตำแหน่งใด ๆ รหัสนี้ก็เพียงพอแล้ว:

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


7

ตั้งโฟกัสก่อนที่คุณจะแทรกข้อความลงใน textarea ดังนั้น?

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

6

สิ่งนี้ใช้ได้กับฉันในโครเมียม

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

เห็นได้ชัดว่าคุณต้องการหน่วงเวลาเป็นไมโครวินาทีหรือมากกว่าเพราะโดยปกติผู้ใช้จะเน้นไปที่ฟิลด์ข้อความโดยคลิกที่ตำแหน่งในฟิลด์ข้อความ (หรือโดยการกดที่แท็บ) ที่คุณต้องการแทนที่ดังนั้นคุณต้องรอจนกว่าตำแหน่งนั้น กำหนดโดยผู้ใช้คลิกแล้วเปลี่ยน


ไม่อนุญาตให้ใช้คุณสมบัติที่กำหนดเป็นแบบอ่านอย่างเดียวในโหมดเข้มงวด
Ivan Rubinson

4

เพียงจำไว้ว่าให้ส่งคืนเท็จหลังจากการเรียกใช้ฟังก์ชันหากคุณใช้ปุ่มลูกศรเนื่องจาก Chrome จะทำหน้าที่เป็นฝ่ายจัดการ

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

2
return false;มันเป็นเรื่องการปฏิบัติที่ดีที่สุดที่จะไม่ คุณต้องการevent.preventDefault();แทน หากคุณส่งคืนเท็จคุณกำลังแสดงถึงevent.stopPropagation()สิ่งที่ไม่เป็นที่ต้องการเสมอ
Alan H.

4

ตามคำถามนี้คำตอบจะไม่สมบูรณ์แบบสำหรับเช่นและโอเปร่าเมื่อมีบรรทัดใหม่ใน textarea คำตอบอธิบายวิธีการปรับ selectionStart ที่ selectionEnd ก่อนที่จะเรียก setSelectionRange

ฉันได้ลอง adjustOffset จากคำถามอื่นด้วยโซลูชันที่เสนอโดย @AVProgrammer และทำงานได้

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

การปรับเปลี่ยนเล็กน้อยกับรหัสที่ฉันพบใน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
jbobbins

3

ฉันต้องทำให้การทำงานนี้เป็นไปได้สำหรับองค์ประกอบที่พึงพอใจและ 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 แต่คุณจะไม่เห็นภาพโฟกัสตั้งแต่จัดตั้งขึ้นที่หน้าต่างคอนโซล

Bests; D


1

คุณสามารถเปลี่ยนต้นแบบโดยตรงได้หาก 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.