Come inserire il testo nell'area di testo nella posizione corrente del cursore?


126

Vorrei creare una semplice funzione che aggiunge testo in un'area di testo alla posizione del cursore dell'utente. Deve essere una funzione pulita. Solo le basi. Posso capire il resto.



2
Date un'occhiata a questa risposta già postato: stackoverflow.com/questions/4456545/...
John Culviner


Interessante articolo del 2018: Come inserire testo
nell'area di

Se stai cercando un modulo semplice con supporto per l'annullamento, prova insert-text-textarea . Se hai bisogno del supporto per IE8 +, prova il pacchetto insert-text-at-cursor .
fregante

Risposte:


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
per correggere "perde la posizione del cursore": aggiungi inserisci queste righe prima di} else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
user340140

10
Grazie Rab per la risposta e @ user340140 per la correzione. Ecco un esempio funzionante .
Znarkus

@ user340140, la tua correzione "perde la potenza del cursore", funziona solo se metto a fuoco l'input subito prima delle linee che suggerisci. Sembra impossibile cambiare la selezione su un campo non focalizzato, almeno in Chrome (versione attuale 62.0)
Jette

C'è un problema minore con questo codice: selectionStartè un valore numerico, quindi dovrebbe essere confrontato 0e non '0'dovrebbe essere usato===
Herohtar

82

Questo frammento potrebbe aiutarti con poche righe di 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);
});

Grande! Funziona anche con 1.6 con piccole modifiche.
Șerban Ghiță

1
Ma non può sostituire il testo selezionato
Sergey Goliney,

@mparkuk: soffre ancora del problema "perde la posizione del cursore" menzionato sopra da user340140. (Scusa, dovrei aggiustarlo, ma ho esaurito il tempo.)
jbobbins

4
Grazie per aver fornito un violino funzionante. L'ho aggiornato per ripristinare anche la posizione del cursore e ne ho fatto un plugin jquery: jsfiddle.net/70gqn153
freedomn-m

Funziona ma il cursore finisce nella posizione sbagliata.
AndroidDev

36

Per il bene di Javascript corretto

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

estensione molto bella! funziona proprio come previsto. Grazie!
Martin Johansson

Soluzione migliore! Grazie
Dima Melnik

4
Non è una buona idea estendere il prototipo di oggetti che non possiedi. Basta renderlo una funzione normale e funziona altrettanto bene.
fregante

Questo cancella il buffer di annullamento per l'elemento di modifica dopo l'impostazione this.value = .... C'è un modo per preservarlo?
c00000fd

19

Nuova risposta:

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

Tuttavia, non sono sicuro del supporto del browser per questo.

Testato in 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>

Vecchia risposta:

Una pura modifica JS della risposta di 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>

Testato in Chrome 47, 81 e Firefox 76.

Se desideri modificare il valore del testo attualmente selezionato mentre digiti nello stesso campo (per un completamento automatico o un effetto simile), passa document.activeElement come primo parametro.

Non è il modo più elegante per farlo, ma è piuttosto semplice.

Usi di esempio:

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

non hai chiuso la riga con >>; <<
Phoenix

4
I punti e virgola @Phoenix sono opzionali in Javascript. Funziona anche senza di loro. Tuttavia, puoi modificare in punto e virgola se lo desideri. Nessun problema.
Jayant Bhawal

3
Ho fatto una demo su JSFiddle. Funziona anche utilizzando Version 54.0.2813.0 canary (64-bit), che è fondamentalmente Chrome Canary 54.0.2813.0. Infine, se vuoi che venga inserito nella casella di testo per ID, usa document.getElementById('insertyourIDhere')al posto di elnella funzione.
haykam

Quale parte della mia risposta non è "puro" JS? Ho dimenticato del C ++ lì dentro?
Erik Aigner

2
Hey @ErikAigner! Colpa mia, non avevo capito che questa domanda aveva risposte da due Erik. Volevo dire Erik Pukinskis. Aggiornerò la risposta per rispecchiarlo meglio.
Jayant Bhawal

15

Una soluzione semplice che funziona su Firefox, Chrome, Opera, Safari e Edge ma probabilmente non funzionerà sui vecchi browser IE.

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

setRangeTextla funzione consente di sostituire la selezione corrente con il testo fornito o, in assenza di selezione, inserire il testo nella posizione del cursore. È supportato solo da Firefox per quanto ne so.

Per altri browser c'è il comando "insertText" che influenza solo l'elemento html attualmente focalizzato e ha lo stesso comportamento di setRangeText

Ispirato parzialmente da questo articolo


Questo è quasi il modo giusto. L'articolo che hai collegato, offre una soluzione completa come pacchetto: inserisci-testo-al-cursore . Comunque lo preferisco execCommandperché supporta undoe rende insert-text-textarea . Nessun supporto per IE ma più piccolo
fregante

1
Purtroppo execCommandè considerato obsoleto da MDN: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Non so perché, sembra essere davvero utile!
Richard

1
Sì, execCommand viene utilizzato per altri browser, per Firefox viene invece utilizzata la funzione setRangeText.
Ramast

Ramast, non è quello che fa il tuo codice. Utilizzerà setRangeText anziché execCommand per qualsiasi browser che lo definisce (la maggior parte). Per il comportamento che descrivi, devi prima chiamare document.execCommand, quindi controllare il valore restituito. Se è falso, utilizza target.setRangeText.
Jools

@Jools se setRangeText è supportato, perché non usarlo al posto di execCommand? Perché devo provare prima execCommand?
Ramast

10

La risposta di Rab funziona alla grande, ma non per Microsoft Edge, quindi ho aggiunto anche un piccolo adattamento per 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

Mi piace il semplice javascript e di solito ho jQuery in giro. Ecco cosa mi è venuto in mente, basato su 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
})

Ecco una 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

Se l'utente non tocca l'input dopo che il testo è stato inserito, l'evento "input" non viene mai attivato e l'attributo value non rifletterà la modifica. Pertanto è importante attivare l'evento di input dopo aver inserito il testo a livello di codice. Focalizzare il campo non è sufficiente.

Quella che segue è una copia della risposta di Snorvarg con un trigger di input alla fine:

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

Ringraziamo plainjs.com per la funzione triggerEvent

Maggiori informazioni sull'evento oninput su w3schools.com

L'ho scoperto mentre creavo un selezionatore di emoji per una chat. Se l'utente seleziona solo alcuni emoji e preme il pulsante "invia", il campo di immissione non viene mai toccato dall'utente. Durante il controllo dell'attributo del valore era sempre vuoto, anche se gli unicode emoji inseriti erano visibili nel campo di input. Si scopre che se l'utente non tocca il campo l'evento "input" non si è mai attivato e la soluzione è stata attivarlo in questo modo. Ci è voluto un po 'per capire questo ... spero che salverà qualcuno un po' di tempo.


0

Inserimento della funzione modificata per riferimento. Questo esempio inserisce un elemento selezionato <select>dall'oggetto e inserisce il cursore tra i 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)
}

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

Sebbene questo possa rispondere alla domanda, è meglio spiegare le parti essenziali della risposta e possibilmente qual era il problema con il codice OP.
pirho

0

Il codice seguente è un adattamento TypeScript del pacchetto https://github.com/grassator/insert-text-at-cursor di 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

Modificato in 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
myfield
Colpirà

2
Wow, davvero troppe ripetizioni document.getElementById(myField)! Fallo una volta in alto e usa un nome di variabile. Quante volte di seguito intendi cercare in modo ridondante lo stesso elemento?
doug65536
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.