Як вставити текст у текстову область на поточній позиції курсору?


125

Я хотів би створити просту функцію, яка додає текст у текстову область на позиції курсора користувача. Це має бути чиста функція. Просто основи. Я можу розібратися з рештою.



2
Погляньте на цей відповідь вже отримано від: stackoverflow.com/questions/4456545 / ...
Джон Culviner



Якщо ви шукаєте простий модуль з підтримкою скасування, спробуйте вставити-текст-textarea . Якщо вам потрібна підтримка IE8 +, спробуйте пакет вставити текст-на-курсор .
fregante

Відповіді:


116
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
щоб виправити "втрачає позицію каретки": додайте вставити ці рядки раніше} else { myField.selectionStart = startPos + myValue.length; myField.selectionEnd = startPos + myValue.length;
user340140

10
Дякую Рабу за відповідь та @ user340140 за виправлення. Ось робочий приклад .
Знаркус

@ user340140, ваше виправлення "втратити карету" працює, працює лише в тому випадку, якщо я зосередиться на введенні прямо перед запропонованими рядками. Здається, неможливо змінити вибір у нецільовому полі, принаймні у Chrome (поточна версія 62.0)
Jette

У цьому коді є незначна проблема: selectionStartце числове значення, і тому його слід порівнювати з, 0а не '0', і, ймовірно, слід використовувати===
Herohtar

82

Цей фрагмент може допомогти вам у ньому в кількох рядках 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);
});

Чудово! Також працює з 1.6 з незначними модифікаціями.
Șerban Ghiță

1
Але він не може замінити вибраний текст
Сергій Голіней

@mparkuk: вона все ще страждає від проблеми "втрачає позицію карет", згаданої вище користувачем340140. (Вибачте, я мушу це виправити, але мені не
вистачало

4
Дякуємо за надання робочої загадки. Я оновив його, щоб також скинути положення карети,
Freedomn-m

Це працює, але курсор виявляється в неправильному місці.
AndroidDev

36

Заради належного 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;
  }
};

дуже приємне розширення! працює так, як очікувалося. Дякую!
Мартін Йоханссон

Найкраще рішення! Дякую
Діма Мельник

4
Розширити прототип об'єктів, якими ви не володієте, не годиться. Просто зробіть це звичайною функцією, і вона працює так само добре.
fregante

Це очищає буфер скасування для елемента редагування після налаштування this.value = .... Чи є спосіб її зберегти?
c00000fd

19

Нова відповідь:

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

Я не впевнений у підтримці браузера для цього.

Випробувано в 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>

Стара відповідь:

Чиста модифікація відповіді Еріка Пукінські:

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 та Firefox 76.

Якщо ви хочете змінити значення поточно вибраного тексту під час набору тексту в одному полі (для автозаповнення чи подібного ефекту), передайте document.activeElement як перший параметр.

Це не найвишуканіший спосіб зробити це, але це досить просто.

Приклад використання:

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

ви не закрили рядок з >>; <<
Фенікс

4
@Phoenix крапки з комою необов’язково в JavaScript. Працює і без них. Хоча ви можете редагувати крапками з комою, якщо хочете. Немає великого.
Jayant Bhawal

3
Я зробив демонстрацію на JSFiddle. Він також працює з використанням Version 54.0.2813.0 canary (64-bit), що в основному є Chrome Canary 54.0.2813.0. Нарешті, якщо ви хочете, щоб він вставив у текстове поле за ідентифікатором, використовуйте document.getElementById('insertyourIDhere')замість elфункції.
хайкам

Яка частина моєї відповіді не є "чистою" JS? Я забув там якийсь C ++?
Ерік Егнер

2
Гей @ErikAigner! Мій поганий, не усвідомлював, що на це запитання були відповіді двох Еріка. Я мав на увазі Erik Pukinskis. Я оновлю відповідь, щоб краще це відобразити.
Jayant Bhawal

15

Просте рішення, яке працює на Firefox, Chrome, Opera, Safari та edge, але, ймовірно, не працюватиме на старих браузерах 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);
  }
}

setRangeTextФункція дозволяє замінити поточний вибір на наданий текст або якщо немає виділення, то вставити текст у позиції курсору. Наскільки він підтримується Firefox, наскільки я знаю.

Для інших браузерів існує команда "insertText", яка впливає лише на html-елемент, який зараз зосереджений і має таку саму поведінку, як і setRangeText

Натхненна частково цією статтею


Це майже вірний шлях. Стаття, яку ви пов’язали, пропонує повне рішення у вигляді пакету: insert-text-at-cursor . Однак я віддаю перевагу, execCommandтому що він підтримує undoі робить вставний текст-textarea . Немає підтримки IE, але менше
fregante

1
На жаль, execCommandMDN вважає застарілим: developer.mozilla.org/en-US/docs/Web/API/Document/execCommand Я не знаю чому, це здається справді корисним!
Річард

1
Так, execCommand використовується для інших браузерів, для firefox замість нього використовується функція setRangeText.
Рамаст

Рамаст, це не те, що робить ваш код. Він використовуватиме setRangeText, а не execCommand для будь-якого браузера, який визначає його (більшість). Для описаної вами поведінки вам потрібно спочатку зателефонувати document.execCommand, а потім перевірити повернене значення. Якщо це неправда, використовуйте target.setRangeText.
Jools

@Jools, якщо setRangeText підтримується, то чому б не використовувати його замість execCommand? Чому мені потрібно спершу спробувати execCommand?
Рамаст

10

Відповідь Раба чудово працює, але не для Microsoft Edge, тому я додав також невелику адаптацію для 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

Мені подобається простий javascript, і зазвичай у мене є jQuery. Ось що я придумав на основі 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
})

Ось демонстрація: 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

Якщо користувач не торкається вводу після введення тексту, подія "введення" ніколи не запускається, а атрибут значення не відображатиме зміни. Тому важливо запустити подію введення після програмного вставлення тексту. Фокусування поля недостатньо.

Далі - копія відповіді Снорварга з вхідним тригером в кінці:

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

Кредит plainjs.com для функції triggerEvent

Більше про події oninput на w3schools.com

Я виявив це під час створення емо-підбирача для чату. Якщо користувач просто вибирає кілька емоджи і натискає кнопку "відправити", користувач ніколи не торкається поля введення. Під час перевірки атрибуту значення він завжди був порожнім, навіть незважаючи на те, що вставлені емодікоди емоції були видні у полі введення. Виявляється, якщо користувач не торкається поля, подія "введення" ніколи не спрацьовує, і рішення полягало в тому, щоб його запустити так. Минуло досить багато часу, щоб розібратися в цьому ... сподіваюся, що це врятує когось деякий час.


0

Опублікування модифікованої функції для власної довідки. Цей приклад вставляє вибраний елемент з <select>об'єкта і ставить каре між тегами:

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

Хоча це може відповісти на питання, краще пояснити суттєві частини відповіді та, можливо, у чому була проблема з кодом ОП.
пірхо

0

Нижче наведений код - адаптація TypeScript пакету https://github.com/grassator/insert-text-at-cursor від Дмитра Кубишкіна.


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

 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
вдасться

2
Нічого, насправді занадто багато повторення document.getElementById(myField)! Зробіть це один раз у верхній частині та використовуйте назву змінної. Скільки разів поспіль ви маєте намір зайво шукати один і той же елемент?
doug65536
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.