Wie füge ich an der aktuellen Cursorposition Text in das Textfeld ein?

123

Ich möchte eine einfache Funktion erstellen, die Text in einen Textbereich an der Cursorposition des Benutzers einfügt. Es muss eine saubere Funktion sein. Nur die Grundlagen. Ich kann den Rest herausfinden.

JoshMWilliams
quelle
3
mögliches Duplikat von Wie füge ich Text ein, wo sich der Cursor befindet?
Derek 18 會 功夫
2
Werfen
John Culviner
Interessanter Artikel aus dem Jahr 2018: Einfügen von Text in Textarea bei Cursor Fast
Delgan
Wenn Sie nach einem einfachen Modul mit Rückgängig-Unterstützung suchen, versuchen Sie , Text-Textbereich einzufügen . Wenn Sie IE8 + -Unterstützung benötigen, probieren Sie das Paket " Text am Cursor einfügen" aus .
Fregante

Antworten:

114
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
quelle
19
um "verliert die Caret-Position" zu beheben: Fügen Sie diese Zeilen vor} else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
user340140
10
Danke Rab für die Antwort und @ user340140 für das Update. Hier ist ein Arbeitsbeispiel .
Znarkus
@ user340140, Ihr Fix "Caret Potition verlieren", funktioniert nur, wenn ich mich direkt vor den von Ihnen vorgeschlagenen Zeilen auf die Eingabe konzentriere. Es scheint unmöglich zu sein, die Auswahl in einem nicht fokussierten Feld zu ändern, zumindest in Chrome (aktuelle Version 62.0)
Jette
Es gibt ein kleines Problem mit diesem Code: selectionStartist ein numerischer Wert und sollte daher mit 0und nicht verglichen '0'werden und sollte wahrscheinlich===
Herohtar
81

Dieses Snippet könnte Ihnen dabei in einigen Zeilen von jQuery 1.9+ helfen: 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
quelle
Toll! Funktioniert auch mit 1.6 mit geringfügigen Änderungen.
Șerban Ghiță
1
Aber es kann nicht ausgewählten Text ersetzen
Sergey Goliney
@mparkuk: Es leidet immer noch unter dem oben von user340140 erwähnten Problem "verliert die Caret-Position". (Entschuldigung, ich sollte es reparieren, aber mir ging die Zeit davon.)
jbobbins
4
Vielen Dank, dass Sie eine funktionierende Geige zur Verfügung gestellt haben. Ich habe es aktualisiert, um auch die Caret-Position zurückzusetzen, und es zu einem JQuery-Plugin gemacht: jsfiddle.net/70gqn153
Freedomn-M
Dies funktioniert, aber der Cursor landet an der falschen Stelle.
AndroidDev
36

Für das richtige Javascript

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
quelle
sehr schöne Erweiterung! funktioniert wie erwartet. Vielen Dank!
Martin Johansson
Beste Lösung! Vielen Dank
Dima Melnik
4
Es ist keine gute Idee, den Prototyp von Objekten zu erweitern, die Sie nicht besitzen. Machen Sie es einfach zu einer regulären Funktion und es funktioniert genauso gut.
fregante
Dadurch wird der Rückgängig-Puffer für das Bearbeitungselement nach dem Einstellen gelöscht this.value = .... Gibt es eine Möglichkeit, es zu bewahren?
c00000fd
17

Neue Antwort:

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

Ich bin mir jedoch nicht sicher, ob der Browser dies unterstützt.

Getestet 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>

Alte Antwort:

Eine reine JS-Modifikation von Erik Pukinskis 'Antwort:

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>

Getestet in Chrome 47, 81 und Firefox 76.

Wenn Sie den Wert des aktuell ausgewählten Texts ändern möchten, während Sie in dasselbe Feld eingeben (für eine automatische Vervollständigung oder einen ähnlichen Effekt), übergeben Sie document.activeElementals ersten Parameter.

Es ist nicht die eleganteste Art, dies zu tun, aber es ist ziemlich einfach.

Beispielverwendungen:

typeInTextarea('hello');
typeInTextarea('haha', document.getElementById('some-id'));
Jayant Bhawal
quelle
Sie haben die Zeile nicht mit >> geschlossen. <<
Phoenix
4
@ Phoix-Semikolons sind in Javascript optional. Funktioniert auch ohne sie. Sie können jedoch auch in Semikolons bearbeiten, wenn Sie möchten. Kein Problem.
Jayant Bhawal
3
Ich habe eine Demo auf JSFiddle gemacht. Es funktioniert auch mit Version 54.0.2813.0 canary (64-bit)Chrome Canary 54.0.2813.0. Wenn Sie möchten, dass es nach ID in das Textfeld eingefügt wird, verwenden Sie es document.getElementById('insertyourIDhere')anstelle von elin der Funktion.
Haykam
Welcher Teil meiner Antwort ist nicht "reines" JS? Habe ich dort etwas C ++ vergessen?
Erik Aigner
2
Hey @ErikAigner! Mein schlechtes, wusste nicht, dass diese Frage von zwei Eriks beantwortet wurde. Ich meinte Erik Pukinskis. Ich werde die Antwort aktualisieren, um dies besser widerzuspiegeln.
Jayant Bhawal
15

Eine einfache Lösung, die auf Firefox, Chrome, Opera, Safari und Edge funktioniert, aber wahrscheinlich nicht auf alten IE-Browsern funktioniert.

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

setRangeTextMit dieser Funktion können Sie die aktuelle Auswahl durch den bereitgestellten Text ersetzen. Wenn keine Auswahl vorhanden ist, fügen Sie den Text an der Cursorposition ein. Soweit ich weiß, wird es nur von Firefox unterstützt.

Für andere Browser gibt es den Befehl "insertText", der sich nur auf das aktuell fokussierte HTML-Element auswirkt und dasselbe Verhalten wie hat setRangeText

Teilweise inspiriert von diesem Artikel

Ramast
quelle
Das ist fast der richtige Weg. Der Artikel, den Sie verlinkt haben, bietet eine vollständige Lösung als Paket: Insert-Text-at-Cursor . Ich bevorzuge execCommandes jedoch, weil es Insert-Text-Textarea unterstützt undound erstellt hat . Keine IE-Unterstützung, aber kleiner
fregante
1
Wird execCommandvon MDN leider als veraltet angesehen: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Ich weiß nicht warum, es scheint wirklich nützlich zu sein!
Richard
1
Ja, execCommand wird für andere Browser verwendet. Für Firefox wird stattdessen die Funktion setRangeText verwendet.
Ramast
Ramast, das macht dein Code nicht. Für jeden Browser, der es definiert (meistens), wird setRangeText anstelle von execCommand verwendet. Für das von Ihnen beschriebene Verhalten müssen Sie zuerst document.execCommand aufrufen und dann den Rückgabewert überprüfen. Wenn es falsch ist, verwenden Sie target.setRangeText.
Jools
@Jools Wenn setRangeText unterstützt wird, warum nicht anstelle von execCommand? Warum muss ich zuerst execCommand ausprobieren?
Ramast
10

Rabs Antwort funktioniert gut, aber nicht für Microsoft Edge. Deshalb habe ich auch eine kleine Anpassung für Edge hinzugefügt:

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
quelle
9

Ich mag einfaches Javascript und habe normalerweise jQuery in der Nähe. Folgendes habe ich mir ausgedacht , basierend auf 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
})

Hier ist eine Demo: http://codepen.io/erikpukinskis/pen/EjaaMY?editors=101

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

quelle
4

Wenn der Benutzer die Eingabe nach dem Einfügen von Text nicht berührt, wird das Ereignis "Eingabe" nie ausgelöst, und das Wertattribut spiegelt die Änderung nicht wider. Daher ist es wichtig, das Eingabeereignis nach dem programmgesteuerten Einfügen von Text auszulösen. Das Feld zu fokussieren reicht nicht aus.

Das Folgende ist eine Kopie von Snorvargs Antwort mit einem Eingabetrigger am Ende:

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

Gutschrift an plainjs.com für die TriggerEvent-Funktion

Mehr zum oninput event auf w3schools.com

Ich habe dies entdeckt, als ich einen Emoji-Picker für einen Chat erstellt habe. Wenn der Benutzer nur einige Emojis auswählt und auf die Schaltfläche "Senden" klickt, wird das Eingabefeld vom Benutzer nie berührt. Bei der Überprüfung des Wertattributs war es immer leer, obwohl die eingefügten Emoji-Unicodes im Eingabefeld sichtbar waren. Es stellt sich heraus, dass, wenn der Benutzer das Feld nicht berührt, das Eingabeereignis nie ausgelöst wurde und die Lösung darin bestand, es so auszulösen. Es hat eine ganze Weile gedauert, um dies herauszufinden ... ich hoffe, es wird jemandem Zeit sparen.

Jette
quelle
0

Veröffentlichen einer geänderten Funktion als eigene Referenz. In diesem Beispiel wird ein ausgewähltes Element aus dem <select>Objekt eingefügt und das Caret zwischen die Tags eingefügt:

//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
quelle
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
quelle
Während dies die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort und möglicherweise das Problem mit dem OP-Code zu erklären.
Pirho
0

Der folgende Code ist eine TypeScript-Anpassung des Pakets https://github.com/grassator/insert-text-at-cursor von 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
quelle
-1

Es wurde in getElementById (myField) geändert.

 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
quelle
3
Das wird das DOM mehr treffen als nötig. Das Speichern myfieldals Lokal ist viel besser für die Leistung
TMan
2
Wow, wirklich viel zu viel Wiederholung von document.getElementById(myField)! Tun Sie es einmal oben und verwenden Sie einen Variablennamen. Wie oft hintereinander möchten Sie dasselbe Element redundant nachschlagen?
Doug65536