【问题标题】:How to insert text into the textarea at the current cursor position?如何在当前光标位置将文本插入文本区域?
【发布时间】:2012-06-20 01:49:40
【问题描述】:

我想创建一个简单的函数,将文本添加到用户光标位置的文本区域。它需要是一个干净的功能。只是基础知识。我可以弄清楚其余的。

【问题讨论】:

标签: javascript textarea


【解决方案1】:

使用selectionStart/selectionEnd properties of the input element(也适用于<textarea>

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

【讨论】:

  • 修复“丢失插入符号位置”:在} else {myField.selectionStart = startPos + myValue.length;myField.selectionEnd = startPos + myValue.length;之前添加插入这些行
  • 感谢 Rab 的回答和@user340140 的修复。这是working example
  • @user340140,您的“丢失插入符药水”修复,仅当我将焦点放在您建议的行之前的输入时才有效。至少在 Chrome(当前版本 62.0)中,似乎不可能更改非重点领域的选择
  • 这段代码有一个小问题:selectionStart 是一个数值,因此应该与0 比较,而不是'0',可能应该使用===
【解决方案2】:

这个 sn-p 可以在几行 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,稍作修改。
  • 但不能替换选中的文字
  • @mparkuk:它仍然遭受 user340140 上面提到的“丢失插入符号位置”问题。 (抱歉,我应该修复它,但我没时间了。)
  • 感谢您提供工作小提琴。我已对其进行了更新以重置插入符号位置并使其成为 jquery 插件:jsfiddle.net/70gqn153
  • 这可行,但光标最终位于错误的位置。
【解决方案3】:

为了正确的 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;
  }
};

【讨论】:

  • 非常好的扩展!按预期工作。谢谢!
  • 最佳解决方案!谢谢
  • 扩展不属于你的对象的原型并不是一个好主意。只要让它成为一个常规函数,它也能正常工作。
  • 这会在设置this.value = ... 后清除编辑元素的撤消缓冲区。有办法保存吗?
  • @ErikAigner 这不对。在 ES6 之前,A.prototype.fn = X 是拥有“类”/继承的唯一方法。仅仅因为你可以扩展你的对象,并不意味着你应该扩展 native 对象。想象一下 10 年前你实现了Array#map,然后Array#map 变成了一个原生 API,但与你的不兼容。现在有人打开您的代码库并看到 [].map() 并假设它是本机 API。你好头痛和错误。
【解决方案4】:

新答案:

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>

旧答案:

对 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'));

【讨论】:

  • 你没有用 >> 关闭行;
  • @Phoenix 分号在 Javascript 中是可选的。没有它们也可以工作。不过,如果需要,您可以用分号进行编辑。没什么大不了的。
  • I made a demo on JSFiddle. 它也可以使用 Version 54.0.2813.0 canary (64-bit),它基本上是 Chrome Canary 54.0.2813.0。最后,如果您希望它按 ID 插入到文本框中,请在函数中使用 document.getElementById('insertyourIDhere') 代替 el
  • 我的回答中哪一部分不是“纯”JS?我是不是忘了里面有一些 C++?
  • 嘿@ErikAigner!我的错,没有意识到这个问题有两个 Erik 的答案。我的意思是Erik Pukinskis。我会更新答案以更好地反映这一点。
【解决方案5】:

一个简单的解决方案,适用于 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具有相同的行为

部分灵感来自article

【讨论】:

  • 这几乎是正确的方法。您链接的文章以软件包的形式提供了完整的解决方案:insert-text-at-cursor。但是我更喜欢execCommand,因为它支持undo 并制作了insert-text-textarea。不支持 IE 但更小
  • 很遗憾,execCommand 被 MDN 认为已过时:developer.mozilla.org/en-US/docs/Web/API/Document/execCommand 不知道为什么,它似乎真的很有用!
  • 是的,execCommand 用于其他浏览器,Firefox 使用 setRangeText 函数。
  • Ramast,这不是您的代码所做的。对于定义它的任何浏览器(大多数),它将使用 setRangeText 而不是 execCommand。对于您描述的行为,您需要先调用 document.execCommand,然后检查返回值。如果为 false,请使用 target.setRangeText。
  • @Jools 如果支持 setRangeText 那么为什么不使用它而不是 execCommand 呢?为什么我需要先尝试 execCommand?
【解决方案6】:

我喜欢简单的 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

【讨论】:

    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

      如果用户在插入文本后没有触摸输入,'input' 事件永远不会触发,value 属性也不会反映变化。因此,在以编程方式插入文本后触发输入事件很重要。专注于该领域是不够的。

      以下是Snorvarg's answer 的副本,末尾带有输入触发器:

      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 函数归功于plainjs.com

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

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

      【讨论】:

      • 这是一个非常有用的提示,谢谢分享。
      【解决方案9】:

      function insertAtCaret(text) {
        const textarea = document.querySelector('textarea')
        textarea.setRangeText(
          text,
          textarea.selectionStart,
          textarea.selectionEnd,
          'end'
        )
      }
      
      setInterval(() => insertAtCaret('Hello'), 3000)
      &lt;textarea cols="60"&gt;Stack Overflow Stack Exchange Starbucks Coffee&lt;/textarea&gt;

      【讨论】:

        【解决方案10】:

        下面的代码是 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;
        }
        
        

        【讨论】:

          【解决方案11】:

          贴出修改后的功能供自己参考。此示例从 &lt;select&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)
          }
          

          【讨论】:

            【解决方案12】:
            /**
             * 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 代码可能存在什么问题。
            【解决方案13】:

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

            【讨论】:

            • 这会比你需要的更多地击中 DOM。将myfield 存储为本地对性能来说要好得多
            • 哇,document.getElementById(myField) 的重复实在太多了!在顶部执行一次并使用变量名。您打算连续多少次冗余查找相同的元素?
            • 感谢您的帮助,我得到了解决方案
            猜你喜欢
            • 2011-05-26
            • 2011-07-09
            • 1970-01-01
            • 2012-10-27
            • 1970-01-01
            • 2020-01-22
            • 2016-04-30
            • 1970-01-01
            相关资源
            最近更新 更多