如何在当前光标位置的文本区域中插入文本?


124

我想创建一个简单的函数,将文本添加到用户光标位置的文本区域。它必须是一个干净的功能。只是基础。我可以找出其余的。



2
看看这个答案已经发布: stackoverflow.com/questions/4456545/...
约翰Culviner


2018年有趣的文章:如何在Cursor Fast
中将

如果您正在寻找一个具有撤消支持的简单模块,请尝试insert-text-textarea。如果需要IE8 +支持,请尝试使用cursor插入文本包。
fregante

Answers:


115
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
感谢Rab的回答,并感谢@ user340140的修复。这是一个有效的例子
Znarkus

@ user340140,您的“丢失插入符号”修复程序,仅当我在您建议的行之前将重点放在输入上时才起作用。至少在Chrome(当前版本为62.0)中,似乎无法更改非重点字段的选择
-Jette

此代码有一个小问题:selectionStart是一个数字值,因此应该0与not 进行比较'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。
banerban Ghiță

1
但是它不能替换选定的文本
Sergey Goliney 2014年

@mparkuk:它仍然遭受user340140上面提到的“丢失插入符位置”问题。(对不起,我应该修复它,但是我没时间了。)
jbobbins 2014年

4
感谢您提供有用的提琴。我已经对其进行了更新,以重置插入符号的位置,并使其成为一个jquery插件:jsfiddle.net/70gqn153
Freedom-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;
  }
};

非常好的扩展!符合预期。谢谢!
马丁·约翰逊

最好的解决方案!谢谢
Dima Melnik '18

4
扩展您不拥有的对象的原型不是一个好主意。只要使其成为常规函数,它就可以正常工作。
fregante

设置后,将清除edit元素的撤消缓冲区this.value = ...。有没有办法保存它?
c00000fd

18

新答案:

https://developer.mozilla.org/zh-CN/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>

旧答案:

Erik Pukinskis的答案的纯JS修改:

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 '16

3
我在JSFiddle上进行了演示。它也可以使用Version 54.0.2813.0 canary (64-bit),基本上是Chrome Canary 54.0.2813.0。最后,如果要按ID将其插入文本框document.getElementById('insertyourIDhere'),请el在函数中使用代替。
haykam'7

我回答的哪一部分不是“纯” JS?我忘了那里的一些C ++吗?
Erik Aigner

2
嘿@ErikAigner!我不好,没意识到这个问题有两个埃里克的答案。我是说Erik Pukinskis。我将更新答案以更好地反映这一点。
Jayant Bhawal

15

一个适用于Firefox,Chrome,Opera,Safari,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并制作了insert-text-textarea。不支持IE,但规模较小
弗雷坎特(Fregante)

1
不幸的是,execCommandMDN认为它已过时:developer.mozilla.org/en-US/docs/Web/API/Document/execCommand我不知道为什么,它似乎非常有用!
理查德

1
是的,execCommand用于其他浏览器,对于Firefox,使用setRangeText函数代替。
拉玛斯

Ramast,那不是您的代码所要做的。它将对所有定义它的浏览器使用setRangeText而不是execCommand。对于您描述的行为,需要首先调用document.execCommand,然后检查返回值。如果为假,请使用target.setRangeText。
乔尔斯

@Jools如果支持setRangeText,那么为什么不使用它而不是execCommand?为什么我需要先尝试execCommand?
拉玛斯

10

Rab的答案很好用,但不适用于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提出的

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

如果用户在插入文本后没有触摸输入,则“输入”事件将永远不会触发,并且value属性不会反映更改。因此,以编程方式插入文本后触发输入事件很重要。仅关注领域是不够的。

以下是Snorvarg答案的副本,最后带有输入触发器:

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功能

w3schools.com上有关oninput事件的更多信息

我在创建表情符号选择器进行聊天时发现了这一点。如果用户仅选择几个表情符号并单击“发送”按钮,则用户永远不会触摸输入字段。检查value属性时,即使在输入字段中可见插入的表情符号unicode,它也始终为空。事实证明,如果用户未触摸该字段,则“ input”事件将永远不会触发,而解决方案就是像这样触发它。花了很长时间才弄清楚这个问题……希望它可以节省一些时间。


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

尽管这可能会回答问题,但最好解释一下答案的基本部分,并可能解释OPs代码的问题所在。
pirho's

0

下面的代码是Dmitriy Kubyshkin的https://github.com/grassator/insert-text-at-cursor包的TypeScript改编版。


/**
 * 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
这将以比您需要的方式更多地影响DOM。myfield作为本地存储,性能要好得多
TMan 2014年

2
哇,真的太多重复了document.getElementById(myField)!在顶部执行一次,然后使用变量名。您打算连续多少次冗余查找同一元素?
doug65536
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.