Làm thế nào để chèn văn bản vào vùng văn bản tại vị trí con trỏ hiện tại?


126

Tôi muốn tạo một chức năng đơn giản để thêm văn bản vào vùng văn bản tại vị trí con trỏ của người dùng. Nó cần phải là một chức năng sạch. Chỉ những điều căn bản. Tôi có thể tìm ra phần còn lại.



2
Hãy xem câu trả lời này đã được đăng: stackoverflow.com/questions/4456545/…
John Culviner.



Nếu bạn đang tìm kiếm một mô-đun đơn giản có hỗ trợ hoàn tác, hãy thử insert-text-textarea . Nếu bạn cần hỗ trợ IE8 +, hãy thử gói chèn văn bản tại con trỏ .
fregante

Câu trả lời:


117
function insertAtCursor(myField, myValue) {
    //IE support
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    //MOZILLA and others
    else if (myField.selectionStart || myField.selectionStart == '0') {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        myField.value = myField.value.substring(0, startPos)
            + myValue
            + myField.value.substring(endPos, myField.value.length);
    } else {
        myField.value += myValue;
    }
}

19
để sửa chữa "mất vị trí dấu nháy": add INSER những dòng này trước} else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
user340140

10
Cảm ơn Rab về câu trả lời và @ user340140 về bản sửa lỗi. Đây là một ví dụ hoạt động .
Znarkus

@ user340140, bản sửa lỗi "mất dấu mũ" của bạn, chỉ hoạt động nếu tôi tập trung vào đầu vào ngay trước các dòng bạn đề xuất. Dường như không thể thay đổi lựa chọn trên một trường không được lấy tiêu điểm, ít nhất là trong Chrome (phiên bản hiện tại 62.0)
Jette

Có một vấn đề nhỏ với mã này: selectionStartlà một giá trị số, và do đó nên được so sánh với 0và không '0', và có lẽ nên sử dụng===
Herohtar

82

Đoạn mã này có thể giúp bạn với nó trong một vài dòng của jQuery 1.9+: http://jsfiddle.net/4MBUG/2/

$('input[type=button]').on('click', function() {
    var cursorPos = $('#text').prop('selectionStart');
    var v = $('#text').val();
    var textBefore = v.substring(0,  cursorPos);
    var textAfter  = v.substring(cursorPos, v.length);

    $('#text').val(textBefore + $(this).val() + textAfter);
});

Tuyệt quá! Cũng hoạt động với 1.6 với các sửa đổi nhỏ.
Șerban Ghiță

1
Nhưng nó không thể thay thế văn bản được lựa chọn
Sergey Goliney

@mparkuk: nó vẫn bị vấn đề "mất vị trí dấu mũ" được đề cập ở trên bởi user340140. (Xin lỗi, tôi nên sửa nó, nhưng tôi đã hết thời gian.)
jbobbins

4
Cảm ơn bạn đã cung cấp một fiddle làm việc. Tôi đã cập nhật nó cũng phải đặt lại vị trí dấu nháy và làm cho nó một plugin jquery: jsfiddle.net/70gqn153
freedomn-m

Điều này hoạt động nhưng con trỏ kết thúc ở vị trí sai.
AndroidDev

36

Vì lợi ích của Javascript phù hợp

HTMLTextAreaElement.prototype.insertAtCaret = function (text) {
  text = text || '';
  if (document.selection) {
    // IE
    this.focus();
    var sel = document.selection.createRange();
    sel.text = text;
  } else if (this.selectionStart || this.selectionStart === 0) {
    // Others
    var startPos = this.selectionStart;
    var endPos = this.selectionEnd;
    this.value = this.value.substring(0, startPos) +
      text +
      this.value.substring(endPos, this.value.length);
    this.selectionStart = startPos + text.length;
    this.selectionEnd = startPos + text.length;
  } else {
    this.value += text;
  }
};

phần mở rộng rất đẹp! hoạt động như mong đợi. Cảm ơn!
Martin Johansson

Giải pháp tốt nhất! Cảm ơn bạn
Dima Melnik

4
Không phải là một ý kiến ​​hay nếu bạn mở rộng nguyên mẫu của những vật thể mà bạn không sở hữu. Chỉ cần làm cho nó trở thành một chức năng thông thường và nó hoạt động tốt.
fregante

Thao tác này sẽ xóa bộ đệm hoàn tác cho phần tử chỉnh sửa sau khi thiết lập this.value = .... Có cách nào để bảo quản không?
c00000fd

19

Câu trả lời mới:

https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setRangeText

Tôi không chắc về sự hỗ trợ của trình duyệt cho việc này.

Đã thử nghiệm trong Chrome 81.

function typeInTextarea(newText, el = document.activeElement) {
  const [start, end] = [el.selectionStart, el.selectionEnd];
  el.setRangeText(newText, start, end, 'select');
}

document.getElementById("input").onkeydown = e => {
  if (e.key === "Enter") typeInTextarea("lol");
}
<input id="input" />
<br/><br/>
<div>Press Enter to insert "lol" at caret.</div>
<div>It'll replace a selection with the given text.</div>

Câu trả lời cũ:

Một sửa đổi JS thuần túy cho câu trả lời của Erik Pukinskis:

function typeInTextarea(newText, el = document.activeElement) {
  const start = el.selectionStart
  const end = el.selectionEnd
  const text = el.value
  const before = text.substring(0, start)
  const after  = text.substring(end, text.length)
  el.value = (before + newText + after)
  el.selectionStart = el.selectionEnd = start + newText.length
  el.focus()
}

document.getElementById("input").onkeydown = e => {
  if (e.key === "Enter") typeInTextarea("lol");
}
<input id="input" />
<br/><br/>
<div>Press Enter to insert "lol" at caret.</div>

Đã thử nghiệm trong Chrome 47, 81 và Firefox 76.

Nếu bạn muốn thay đổi giá trị của văn bản hiện đang được chọn trong khi bạn đang nhập trong cùng một trường (để có hiệu ứng tự động hoàn thành hoặc tương tự), hãy chuyển document.activeElement làm tham số đầu tiên.

Đây không phải là cách thanh lịch nhất để làm điều này, nhưng nó khá đơn giản.

Cách sử dụng ví dụ:

typeInTextarea('hello');
typeInTextarea('haha', document.getElementById('some-id'));

bạn đã không đóng dòng bằng >>; <<
Phoenix

4
Dấu chấm phẩy @Phoenix là tùy chọn trong Javascript. Hoạt động mà không có chúng quá. Mặc dù vậy, bạn có thể chỉnh sửa bằng dấu chấm phẩy nếu muốn. Không có vấn đề gì.
Jayant Bhawal

3
Tôi đã tạo bản demo trên JSFiddle. Nó cũng hoạt động bằng cách sử dụng Version 54.0.2813.0 canary (64-bit), về cơ bản là Chrome Canary 54.0.2813.0. Cuối cùng, nếu bạn muốn nó chèn vào hộp văn bản bằng ID, hãy sử dụng document.getElementById('insertyourIDhere')thay elcho hàm.
haykam,

Phần nào trong câu trả lời của tôi không phải là JS "nguyên chất"? Tôi đã quên một số C ++ trong đó?
Erik Aigner

2
Xin chào @ErikAigner! Thật tệ, tôi không nhận ra câu hỏi này đã có câu trả lời của hai Erik. Ý tôi là Erik Pukinskis. Tôi sẽ cập nhật câu trả lời để phản ánh điều đó tốt hơn.
Jayant Bhawal

15

Một giải pháp đơn giản hoạt động trên firefox, chrome, opera, safari và edge nhưng có thể sẽ không hoạt động trên các trình duyệt IE cũ.

  var target = document.getElementById("mytextarea_id")

  if (target.setRangeText) {
     //if setRangeText function is supported by current browser
     target.setRangeText(data)
  } else {
    target.focus()
    document.execCommand('insertText', false /*no UI*/, data);
  }
}

setRangeTextchức năng cho phép bạn thay thế lựa chọn hiện tại bằng văn bản được cung cấp hoặc nếu không có lựa chọn nào thì chèn văn bản vào vị trí con trỏ. Nó chỉ được hỗ trợ bởi firefox theo như tôi biết.

Đối với các trình duyệt khác, có lệnh "insertText" chỉ ảnh hưởng đến phần tử html hiện được tập trung và có hành vi tương tự như setRangeText

Lấy cảm hứng một phần từ bài viết này


Đây gần như là cách đúng đắn. Bài viết bạn đã liên kết, cung cấp một giải pháp đầy đủ dưới dạng một gói: insert-text-at-cursor . Tuy nhiên, tôi thích hơn execCommandvì nó hỗ trợ undovà tạo chèn-văn bản-văn bản . Không hỗ trợ IE nhưng nhỏ hơn
fregante

1
Thật không may, execCommandđược coi là lỗi thời bởi MDN: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Tôi không biết tại sao, nó có vẻ thực sự hữu ích!
Richard

1
Có, executeCommand được sử dụng cho các trình duyệt khác, đối với firefox, hàm setRangeText được sử dụng thay thế.
Ramast

Ramast, đó không phải là những gì mã của bạn làm. Nó sẽ sử dụng setRangeText chứ không phải là executeCommand cho bất kỳ trình duyệt nào xác định nó (hầu hết). Đối với hành vi bạn mô tả, trước tiên bạn cần gọi document.execCommand, sau đó kiểm tra giá trị trả về. Nếu nó sai, hãy sử dụng target.setRangeText.
Jools

@Jools nếu setRangeText được hỗ trợ thì tại sao lại không sử dụng nó thay vì executeCommand? Tại sao tôi cần phải dùng thử executeCommand trước?
Ramast

10

Câu trả lời của Rab hoạt động tốt, nhưng không phù hợp với Microsoft Edge, vì vậy tôi cũng đã thêm một bản điều chỉnh nhỏ cho Edge:

https://jsfiddle.net/et9borp4/

function insertAtCursor(myField, myValue) {
    //IE support
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    // Microsoft Edge
    else if(window.navigator.userAgent.indexOf("Edge") > -1) {
      var startPos = myField.selectionStart; 
      var endPos = myField.selectionEnd; 

      myField.value = myField.value.substring(0, startPos)+ myValue 
             + myField.value.substring(endPos, myField.value.length); 

      var pos = startPos + myValue.length;
      myField.focus();
      myField.setSelectionRange(pos, pos);
    }
    //MOZILLA and others
    else if (myField.selectionStart || myField.selectionStart == '0') {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        myField.value = myField.value.substring(0, startPos)
            + myValue
            + myField.value.substring(endPos, myField.value.length);
    } else {
        myField.value += myValue;
    }
}

9

Tôi thích javascript đơn giản và tôi thường có jQuery xung quanh. Đây là những gì tôi nghĩ ra, dựa trên mparkuk's :

function typeInTextarea(el, newText) {
  var start = el.prop("selectionStart")
  var end = el.prop("selectionEnd")
  var text = el.val()
  var before = text.substring(0, start)
  var after  = text.substring(end, text.length)
  el.val(before + newText + after)
  el[0].selectionStart = el[0].selectionEnd = start + newText.length
  el.focus()
}

$("button").on("click", function() {
  typeInTextarea($("textarea"), "some text")
  return false
})

Đây là bản demo: http://codepen.io/erikpukinskis/pen/EjaaMY?editors=101


6

function insertAtCaret(text) {
  const textarea = document.querySelector('textarea')
  textarea.setRangeText(
    text,
    textarea.selectionStart,
    textarea.selectionEnd,
    'end'
  )
}

setInterval(() => insertAtCaret('Hello'), 3000)
<textarea cols="60">Stack Overflow Stack Exchange Starbucks Coffee</textarea>

4

Nếu người dùng không chạm vào đầu vào sau khi chèn văn bản, thì sự kiện 'đầu vào' sẽ không bao giờ được kích hoạt và thuộc tính giá trị sẽ không phản ánh thay đổi. Do đó, điều quan trọng là phải kích hoạt sự kiện đầu vào sau khi chèn văn bản theo chương trình. Tập trung lĩnh vực là không đủ.

Sau đây là bản sao câu trả lời của Snorvarg với một trình kích hoạt đầu vào ở cuối:

function insertAtCursor(myField, myValue) {
    //IE support
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    // Microsoft Edge
    else if(window.navigator.userAgent.indexOf("Edge") > -1) {
      var startPos = myField.selectionStart; 
      var endPos = myField.selectionEnd; 

      myField.value = myField.value.substring(0, startPos)+ myValue 
             + myField.value.substring(endPos, myField.value.length); 

      var pos = startPos + myValue.length;
      myField.focus();
      myField.setSelectionRange(pos, pos);
    }
    //MOZILLA and others
    else if (myField.selectionStart || myField.selectionStart == '0') {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        myField.value = myField.value.substring(0, startPos)
            + myValue
            + myField.value.substring(endPos, myField.value.length);
    } else {
        myField.value += myValue;
    }
    triggerEvent(myField,'input');
}

function triggerEvent(el, type){
  if ('createEvent' in document) {
    // modern browsers, IE9+
    var e = document.createEvent('HTMLEvents');
    e.initEvent(type, false, true);
    el.dispatchEvent(e);
  } else {
    // IE 8
    var e = document.createEventObject();
    e.eventType = type;
    el.fireEvent('on'+e.eventType, e);
  }
}

Tín dụng để plainjs.com cho hàm triggerEvent

Thông tin thêm về sự kiện oninput tại w3schools.com

Tôi đã phát hiện ra điều này trong khi tạo bộ chọn biểu tượng cảm xúc cho một cuộc trò chuyện. Nếu người dùng chỉ cần chọn một vài biểu tượng cảm xúc và nhấn nút "gửi", trường nhập liệu sẽ không bao giờ được người dùng chạm vào. Khi kiểm tra thuộc tính giá trị, thuộc tính này luôn trống, mặc dù các mã đơn biểu tượng cảm xúc được chèn vẫn hiển thị trong trường nhập. Hóa ra rằng nếu người dùng không chạm vào trường, sự kiện 'đầu vào' sẽ không bao giờ được kích hoạt và giải pháp là kích hoạt nó như thế này. Phải mất khá nhiều thời gian để tìm ra điều này ... hy vọng nó sẽ giúp ai đó tiết kiệm thời gian.


0

Đăng chức năng đã sửa đổi để tham khảo riêng. Ví dụ này chèn một mục đã chọn từ <select>đối tượng và đặt dấu mũ giữa các thẻ:

//Inserts a choicebox selected element into target by id
function insertTag(choicebox,id) {
    var ta=document.getElementById(id)
    ta.focus()
    var ss=ta.selectionStart
    var se=ta.selectionEnd
    ta.value=ta.value.substring(0,ss)+'<'+choicebox.value+'>'+'</'+choicebox.value+'>'+ta.value.substring(se,ta.value.length)
    ta.setSelectionRange(ss+choicebox.value.length+2,ss+choicebox.value.length+2)
}

0
 /**
 * Usage "foo baz".insertInside(4, 0, "bar ") ==> "foo bar baz"
 */
String.prototype.insertInside = function(start, delCount, newSubStr) {
    return this.slice(0, start) + newSubStr + this.slice(start + Math.abs(delCount));
};


 $('textarea').bind("keydown keypress", function (event) {
   var val = $(this).val();
   var indexOf = $(this).prop('selectionStart');
   if(event.which === 13) {
       val = val.insertInside(indexOf, 0,  "<br>\n");
       $(this).val(val);
       $(this).focus();
    }
})

Mặc dù điều này có thể trả lời câu hỏi, nhưng tốt hơn là giải thích các phần thiết yếu của câu trả lời và có thể vấn đề với mã OP là gì.
pirho

0

Đoạn mã dưới đây là bản chuyển thể TypeScript của gói https://github.com/grassator/insert-text-at-cursor của Dmitriy Kubyshkin.


/**
 * Inserts the given text at the cursor. If the element contains a selection, the selection
 * will be replaced by the text.
 */
export function insertText(input: HTMLTextAreaElement | HTMLInputElement, text: string) {
  // Most of the used APIs only work with the field selected
  input.focus();

  // IE 8-10
  if ((document as any).selection) {
    const ieRange = (document as any).selection.createRange();
    ieRange.text = text;

    // Move cursor after the inserted text
    ieRange.collapse(false /* to the end */);
    ieRange.select();

    return;
  }

  // Webkit + Edge
  const isSuccess = document.execCommand("insertText", false, text);
  if (!isSuccess) {
    const start = input.selectionStart;
    const end = input.selectionEnd;
    // Firefox (non-standard method)
    if (typeof (input as any).setRangeText === "function") {
      (input as any).setRangeText(text);
    } else {
      if (canManipulateViaTextNodes(input)) {
        const textNode = document.createTextNode(text);
        let node = input.firstChild;

        // If textarea is empty, just insert the text
        if (!node) {
          input.appendChild(textNode);
        } else {
          // Otherwise we need to find a nodes for start and end
          let offset = 0;
          let startNode = null;
          let endNode = null;

          // To make a change we just need a Range, not a Selection
          const range = document.createRange();

          while (node && (startNode === null || endNode === null)) {
            const nodeLength = node.nodeValue.length;

            // if start of the selection falls into current node
            if (start >= offset && start <= offset + nodeLength) {
              range.setStart((startNode = node), start - offset);
            }

            // if end of the selection falls into current node
            if (end >= offset && end <= offset + nodeLength) {
              range.setEnd((endNode = node), end - offset);
            }

            offset += nodeLength;
            node = node.nextSibling;
          }

          // If there is some text selected, remove it as we should replace it
          if (start !== end) {
            range.deleteContents();
          }

          // Finally insert a new node. The browser will automatically
          // split start and end nodes into two if necessary
          range.insertNode(textNode);
        }
      } else {
        // For the text input the only way is to replace the whole value :(
        const value = input.value;
        input.value = value.slice(0, start) + text + value.slice(end);
      }
    }

    // Correct the cursor position to be at the end of the insertion
    input.setSelectionRange(start + text.length, start + text.length);

    // Notify any possible listeners of the change
    const e = document.createEvent("UIEvent");
    e.initEvent("input", true, false);
    input.dispatchEvent(e);
  }
}

function canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement) {
  if (input.nodeName !== "TEXTAREA") {
    return false;
  }
  let browserSupportsTextareaTextNodes;
  if (typeof browserSupportsTextareaTextNodes === "undefined") {
    const textarea = document.createElement("textarea");
    textarea.value = "1";
    browserSupportsTextareaTextNodes = !!textarea.firstChild;
  }
  return browserSupportsTextareaTextNodes;
}

-1

Đã thay đổi nó thành getElementById (myField)

 function insertAtCursor(myField, myValue) {
    //IE support
    if (document.selection) {
        document.getElementById(myField).focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    //MOZILLA and others
    else if (document.getElementById(myField).selectionStart || document.getElementById(myField).selectionStart == '0') {
        var startPos = document.getElementById(myField).selectionStart;
        var endPos = document.getElementById(myField).selectionEnd;
        document.getElementById(myField).value = document.getElementById(myField).value.substring(0, startPos)
            + myValue
            + document.getElementById(myField).value.substring(endPos, document.getElementById(myField).value.length);
    } else {
        document.getElementById(myField).value += myValue;
    }
}

3
Điều đó sẽ tấn công DOM nhiều hơn những gì bạn cần .. lưu trữ myfielddưới dạng cục bộ sẽ tốt hơn nhiều cho hiệu suất
TMan

2
Wow, thực sự là quá nhiều sự lặp lại document.getElementById(myField)! Làm điều đó một lần ở trên cùng và sử dụng một tên biến. Bạn định tìm kiếm dư thừa cùng một phần tử bao nhiêu lần liên tiếp?
doug65536
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.