Mevcut imleç konumunda metin alanına metin nasıl eklenir?


126

Kullanıcının imleç konumunda bir metin alanına metin ekleyen basit bir işlev oluşturmak istiyorum. Temiz bir işlev olması gerekir. Sadece temel bilgiler. Gerisini çözebilirim.



2
Daha önce gönderilmiş olan bu cevaba bir göz atın: stackoverflow.com/questions/4456545/…
John Culviner


İlginç 2018 makalesi: Hızlı
İmleçte

Geri alma desteğine sahip basit bir modül arıyorsanız, metin-metin alanını deneyin . IE8 + desteğine ihtiyacınız varsa imleçte metin ekle paketini deneyin .
fregante

Yanıtlar:


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
"imleç konumunu kaybediyor" } else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
hatasını

10
Cevap için teşekkürler Rab ve düzeltme için @ user340140. İşte çalışan bir örnek .
Znarkus

@ user340140, sizin "silme işaretini kaybetme" düzeltmeniz, yalnızca önerdiğiniz satırlardan hemen önce girdiye odaklanırsam işe yarar. Odaklanmayan bir alanda seçimi değiştirmek imkansız görünüyor, en azından Chrome'da (şu anki sürüm 62.0)
Jette

Bu kodla ilgili küçük bir sorun var: selectionStartsayısal bir değerdir ve bu nedenle karşılaştırılmalı 0ve karşılaştırılmamalıdır '0've muhtemelen kullanılmalıdır===
Herohtar

82

Bu pasaj, birkaç satır jQuery 1.9+ ile size yardımcı olabilir: 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);
});

Harika! Ayrıca küçük değişikliklerle 1.6 ile çalışır.
Șerban Ghiță

1
Ancak seçilen metni değiştiremez
Sergey Goliney

@mparkuk: yukarıda user340140 tarafından bahsedilen "düzeltme konumunu kaybetti" sorunundan hala muzdarip. (Üzgünüm, düzeltmeliyim ama zamanım
doldu

4
Çalışan bir keman sağladığınız için teşekkür ederiz. İmleç
Freedomn-m

Bu işe yarıyor, ancak imleç yanlış konumda sona eriyor.
AndroidDev

36

Doğru Javascript uğruna

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

çok güzel uzantı! beklendiği gibi çalışır. Teşekkürler!
Martin Johansson

En iyi çözüm! Teşekkürler
Dima Melnik

4
Sahip olmadığınız nesnelerin prototipini genişletmek iyi bir fikir değildir. Sadece onu normal bir işlev haline getirin ve aynı şekilde çalışır.
fregante

Bu, ayarlamadan sonra düzenleme öğesi için geri alma arabelleğini temizler this.value = .... Onu korumanın bir yolu var mı?
c00000fd

19

Yeni cevap:

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

Yine de bunun için tarayıcı desteğinden emin değilim.

Chrome 81'de test edilmiştir.

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>

Eski cevap:

Erik Pukinskis'in cevabının saf bir JS değişikliği:

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>

Chrome 47, 81 ve Firefox 76'da test edilmiştir.

Aynı alana yazarken o anda seçili metnin değerini değiştirmek isterseniz (otomatik tamamlama veya benzer bir efekt için), document.activeElementilk parametre olarak iletin.

Bunu yapmanın en zarif yolu bu değil ama oldukça basit.

Örnek kullanımlar:

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

satırı >> ile kapatmadınız; <<
Phoenix

4
@Phoenix noktalı virgül Javascript'te isteğe bağlıdır. Onlar olmadan da çalışır. Yine de, isterseniz noktalı virgülle düzenleyebilirsiniz. Önemli değil.
Jayant Bhawal

3
JSFiddle üzerinde bir demo yaptım. Ayrıca Version 54.0.2813.0 canary (64-bit), temelde Chrome Canary 54.0.2813.0 olan kullanarak da çalışır . Son olarak, metin kutusuna kimliğe göre eklenmesini istiyorsanız , işlevin document.getElementById('insertyourIDhere')yerine kullanın el.
haykam

Cevabımın hangi kısmı "saf" JS değil? Orada biraz C ++ unuttum mu?
Erik Aigner

2
Hey @ErikAigner! Benim hatam, bu sorunun iki Erik'in cevapları olduğunu bilmiyordum. Demek istedim Erik Pukinskis. Bunu daha iyi yansıtmak için cevabı güncelleyeceğim.
Jayant Bhawal

15

Firefox, chrome, opera, safari ve edge üzerinde çalışan ancak muhtemelen eski IE tarayıcılarda çalışmayacak basit bir çözüm.

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

setRangeTextişlevi mevcut seçimi sağlanan metinle değiştirmenize veya seçim yoksa imleç konumuna metni eklemenize izin verir. Bildiğim kadarıyla sadece firefox tarafından destekleniyor.

Diğer tarayıcılar için, yalnızca şu anda odaklanmış html öğesini etkileyen ve aynı davranışa sahip "insertText" komutu vardır. setRangeText

Bu makaleden kısmen esinlenildi


Bu neredeyse doğru yoldur. Bağladığınız makale, paket olarak tam bir çözüm sunar: imleçte metin ekle . Ancak metin-metin alanınıexecCommand desteklediği undove yaptığı için tercih ediyorum . IE desteği yok ama daha küçük
fregante

1
Maalesef, execCommandMDN tarafından eski kabul ediliyor: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Neden bilmiyorum, gerçekten yararlı görünüyor!
Richard

1
Evet, execCommand diğer tarayıcılarda kullanılır, firefox için bunun yerine setRangeText işlevi kullanılır.
Ramast

Ramast, kodunuzun yaptığı bu değil. Onu tanımlayan herhangi bir tarayıcı için (çoğu) execCommand yerine setRangeText kullanacaktır. Tanımladığınız davranış için önce document.execCommand'i çağırmanız, ardından dönüş değerini kontrol etmeniz gerekir. Yanlışsa target.setRangeText kullanın.
Jools

@Jools eğer setRangeText destekleniyorsa, o zaman neden execCommand yerine onu kullanmıyorsunuz? Neden önce execCommand'i denemem gerekiyor?
Ramast

10

Rab'ın cevabı harika çalışıyor, ancak Microsoft Edge için değil, bu yüzden Edge için de küçük bir uyarlama ekledim:

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

Basit javascript'i seviyorum ve genellikle etrafımda jQuery var. İşte mparkuk'lara dayanarak bulduğum şey :

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

İşte bir 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

Kullanıcı, metin eklendikten sonra girişe dokunmazsa, "giriş" olayı asla tetiklenmez ve değer özelliği değişikliği yansıtmaz. Bu nedenle, programlı olarak metin ekledikten sonra giriş olayını tetiklemek önemlidir. Alana odaklanmak yeterli değil.

Aşağıda Snorvarg'ın yanıtının bir kopyası ve sonunda bir giriş tetikleyicisi bulunmaktadır:

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

TriggerEvent işlevi için plainjs.com'a kredi

Daha etkinlikte oninput yaklaşık olarak w3schools.com

Bunu bir sohbet için emoji seçici oluştururken keşfettim. Kullanıcı yalnızca birkaç emoji seçip "gönder" düğmesine basarsa, giriş alanına kullanıcı hiçbir zaman dokunmaz. Değer özniteliğini kontrol ederken, eklenen emoji unicodes giriş alanında görünmesine rağmen her zaman boştu. Kullanıcı alana dokunmazsa, "giriş" olayının asla tetiklenmediği ve çözümün onu bu şekilde tetiklediği ortaya çıktı. Bunu anlamak epey zaman aldı ... umarım birine biraz zaman kazandırır.


0

Kendi referansı için değiştirilmiş işlevi gönderme. Bu örnek, <select>nesneden seçilen bir öğeyi ekler ve imleci etiketlerin arasına yerleştirir:

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

Bu soruya cevap verebilirken, cevabın temel kısımlarını ve muhtemelen OPs koduyla ilgili sorunun ne olduğunu açıklamak daha iyidir.
pirho

0

Aşağıdaki kod, Dmitriy Kubyshkin tarafından https://github.com/grassator/insert-text-at-cursor paketinin TypeScript uyarlamasıdır .


/**
 * 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

GetElementById (myField) olarak değiştirildi

 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
Bu, DOM'a ihtiyaç duyduğunuzdan daha fazla isabet edecek .. myfieldyerel olarak depolamak performans için çok daha iyi
TMan

2
Vay canına, gerçekten çok fazla tekrarı document.getElementById(myField)! En üstte bir kez yapın ve bir değişken adı kullanın. Aynı öğeyi art arda kaç kez aramayı düşünüyorsunuz?
doug65536
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.