Bagaimana cara memasukkan teks ke dalam area teks pada posisi kursor saat ini?

126

Saya ingin membuat fungsi sederhana yang menambahkan teks ke dalam area teks pada posisi kursor pengguna. Itu harus menjadi fungsi yang bersih. Hanya dasar-dasarnya. Saya bisa mencari tahu sisanya.

JoshMWilliams
sumber
3
kemungkinan duplikat Bagaimana cara menyisipkan beberapa teks di mana kursor berada?
Derek 朕 會 功夫
2
Lihatlah jawaban ini sudah diposting: stackoverflow.com/questions/4456545/…
John Culviner
Artikel 2018 yang menarik: Cara Memasukkan Teks Ke Textarea di Cursor Fast
Delgan
Jika Anda mencari modul sederhana dengan dukungan undo, coba sisipkan-teks-area teks . Jika Anda membutuhkan dukungan IE8 +, coba paket insert-text-at-cursor .
fregante

Jawaban:

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;
    }
}
Raab
sumber
19
untuk memperbaiki "kehilangan posisi tanda sisipan": tambahkan baris ini sebelumnya} else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
pengguna340140
10
Terima kasih Rab untuk jawabannya dan @ user340140 untuk perbaikannya. Berikut adalah contoh yang berfungsi .
Znarkus
@ user340140, perbaikan "potensi tanda sisipan yang hilang", hanya berfungsi jika saya memberikan fokus pada masukan tepat sebelum baris yang Anda sarankan. Tampaknya tidak mungkin untuk mengubah pilihan pada bidang yang tidak fokus, setidaknya di Chrome (versi 62.0 saat ini)
Jette
Ada masalah kecil dengan kode ini: selectionStartadalah nilai numerik, dan karenanya harus dibandingkan dengan 0dan tidak '0', dan mungkin harus menggunakan===
Herohtar
82

Cuplikan ini dapat membantu Anda dalam beberapa baris 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);
});
Adriano Alves
sumber
Bagus! Juga bekerja dengan 1.6 dengan sedikit modifikasi.
Șerban Ghiță
1
Tapi itu tidak bisa menggantikan teks yang dipilih
Sergey Goliney
@mparkuk: masih menderita masalah "kehilangan posisi tanda sisipan" yang disebutkan di atas oleh pengguna340140. (Maaf, saya harus memperbaikinya, tetapi saya kehabisan waktu.)
jbobbins
4
Terima kasih telah menyediakan biola yang berfungsi. Saya telah memperbaruinya untuk juga mengatur ulang posisi tanda sisipan dan menjadikannya plugin jquery: jsfiddle.net/70gqn153
freedomn-m
Ini berfungsi tetapi kursor berakhir di lokasi yang salah.
AndroidDev
36

Demi Javascript yang tepat

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;
  }
};
Erik Aigner
sumber
ekstensi yang sangat bagus! bekerja seperti yang diharapkan. Terima kasih!
Martin Johansson
Solusi terbaik! Terima kasih
Dima Melnik
4
Bukan ide yang baik untuk memperluas prototipe objek yang tidak Anda miliki. Jadikan saja sebagai fungsi biasa dan berfungsi dengan baik.
fregante
Ini menghapus buffer undo untuk elemen edit setelah pengaturan this.value = .... Apakah ada cara untuk melestarikannya?
c00000fd
19

Jawaban baru:

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

Saya tidak yakin tentang dukungan browser untuk ini.

Diuji di 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>

Jawaban lama:

Modifikasi JS murni dari jawaban 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>

Diuji di Chrome 47, 81, dan Firefox 76.

Jika Anda ingin mengubah nilai teks yang saat ini dipilih saat Anda mengetik di bidang yang sama (untuk pelengkapan otomatis atau efek serupa), teruskan document.activeElement sebagai parameter pertama.

Ini bukan cara paling elegan untuk melakukan ini, tetapi cukup sederhana.

Contoh penggunaan:

typeInTextarea('hello');
typeInTextarea('haha', document.getElementById('some-id'));
Jayant Bhawal
sumber
Anda tidak menutup baris dengan >>; <<
Phoenix
4
@Phoenix titik koma adalah opsional dalam Javascript. Bekerja tanpa mereka juga. Meskipun, Anda dapat mengedit dalam titik koma jika Anda mau. Bukan masalah besar.
Jayant Bhawal
3
Saya membuat demo di JSFiddle. Ia juga bekerja menggunakan Version 54.0.2813.0 canary (64-bit), yang pada dasarnya adalah Chrome Canary 54.0.2813.0. Akhirnya, jika Anda ingin memasukkannya ke dalam kotak teks berdasarkan ID, gunakan document.getElementById('insertyourIDhere')di tempat elfungsi.
haykam
Bagian mana dari jawaban saya yang bukan JS "murni"? Apakah saya lupa C ++ di sana?
Erik Aigner
2
Hai @ErikAigner! Sayangnya, saya tidak menyadari pertanyaan ini dijawab oleh dua orang Erik. Maksud saya Erik Pukinskis. Saya akan memperbarui jawaban untuk lebih mencerminkan itu.
Jayant Bhawal
15

Solusi sederhana yang berfungsi di firefox, chrome, opera, safari, dan edge, tetapi mungkin tidak berfungsi di browser IE lama.

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

setRangeTextfungsi memungkinkan Anda untuk mengganti pilihan saat ini dengan teks yang disediakan atau jika tidak ada pilihan maka masukkan teks pada posisi kursor. Ini hanya didukung oleh firefox sejauh yang saya tahu.

Untuk browser lain ada perintah "insertText" yang hanya mempengaruhi elemen html yang saat ini difokuskan dan memiliki perilaku yang sama seperti setRangeText

Terinspirasi sebagian oleh artikel ini

Ramast
sumber
Ini hampir benar. Artikel yang Anda tautkan, menawarkan solusi lengkap sebagai satu paket: sisipkan-teks-di-kursor . Namun saya lebih suka execCommandkarena mendukung undodan membuat insert-text-textarea . Tidak ada dukungan IE tetapi lebih kecil
fregante
1
Sayangnya, execCommanddianggap usang oleh MDN: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Saya tidak tahu mengapa, ini tampaknya sangat berguna!
Richard
1
Ya, execCommand digunakan untuk browser lain, untuk firefox fungsi setRangeText digunakan sebagai gantinya.
Ramast
Ramast, bukan itu yang dilakukan kode Anda. Ini akan menggunakan setRangeText daripada execCommand untuk browser apa pun yang mendefinisikannya (sebagian besar). Untuk perilaku yang Anda gambarkan, Anda perlu memanggil document.execCommand terlebih dahulu, lalu periksa nilai yang dikembalikan. Jika salah, gunakan target.setRangeText.
Jools
@ Jools jika setRangeText didukung, mengapa tidak menggunakannya sebagai ganti execCommand? Mengapa saya perlu mencoba execCommand terlebih dahulu?
Ramast
10

Jawaban Rab berfungsi dengan baik, tetapi tidak untuk Microsoft Edge, jadi saya menambahkan adaptasi kecil untuk Edge juga:

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;
    }
}
Snorvarg
sumber
9

Saya suka javascript sederhana, dan saya biasanya memiliki jQuery. Inilah yang saya dapatkan, berdasarkan mparkuk :

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

Berikut demonya: http://codepen.io/erikpukinskis/pen/EjaaMY?editors=101

Erik Pukinskis
sumber
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>

sumber
4

Jika pengguna tidak menyentuh input setelah teks dimasukkan, peristiwa 'input' tidak pernah dipicu, dan atribut nilai tidak akan mencerminkan perubahan. Oleh karena itu, penting untuk memicu peristiwa masukan setelah memasukkan teks secara terprogram. Memfokuskan pada bidang saja tidak cukup.

Berikut ini adalah salinan jawaban Snorvarg dengan pemicu masukan di akhir:

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

Kredit ke plainjs.com untuk fungsi triggerEvent

Selengkapnya tentang acara oninput di w3schools.com

Saya menemukan ini saat membuat pemetik emoji untuk obrolan. Jika pengguna hanya memilih beberapa emoji dan menekan tombol "kirim", bidang input tidak akan pernah disentuh oleh pengguna. Saat memeriksa atribut value, kolom tersebut selalu kosong, meskipun unicode emoji yang dimasukkan terlihat di kolom input. Ternyata jika pengguna tidak menyentuh bidang, peristiwa 'input' tidak pernah diaktifkan dan solusinya adalah memicunya seperti ini. Butuh waktu cukup lama untuk memikirkan yang satu ini ... semoga bisa menghemat waktu.

Jette
sumber
0

Memposting fungsi yang dimodifikasi untuk referensi sendiri. Contoh ini menyisipkan item yang dipilih dari <select>objek dan meletakkan tanda sisipan di antara tag:

//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)
}
Alexiy
sumber
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();
    }
})
zaarour
sumber
Meskipun ini mungkin menjawab pertanyaan, lebih baik menjelaskan bagian penting dari jawaban dan mungkin apa masalah dengan kode OPs.
pirho
0

Kode di bawah ini adalah adaptasi TypeScript dari paket https://github.com/grassator/insert-text-at-cursor oleh 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;
}
André Pena
sumber
-1

Mengubahnya menjadi 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;
    }
}
tekagami
sumber
3
Itu akan menghantam DOM lebih dari yang Anda butuhkan .. menyimpan myfieldsebagai lokal jauh lebih baik untuk kinerja
TMan
2
Wow, benar-benar terlalu banyak pengulangan document.getElementById(myField)! Lakukan sekali di atas dan gunakan nama variabel. Berapa kali berturut-turut Anda berniat mencari elemen yang sama secara berulang?
doug65536