【问题标题】:Get cursor position when a file is dropped in textarea in Chrome将文件拖放到 Chrome 的 textarea 中时获取光标位置
【发布时间】:2021-01-10 14:41:12
【问题描述】:

当您从 OS 文件系统中将文件拖到文本区域或文本输入上时,鼠标指针附近会出现一个光标(这与 positionStart 不同),向用户显示将插入拖动的内容的位置。

更新:这是一张图片,我正在将一个文件 (test.sh) 拖到文本输入上。如果在“文本”字的中间,您可以看到放置光标。选择光标位于字符串的末尾(在此图片上不可见)。

(Chrome 的默认行为是打开拖放的文件,但我在 drop 事件中覆盖了此行为。我想在 textarea 中插入文件名。)

drop 出现时,我正试图获得这个位置(根据 textarea 值字符串中的索引)。有什么想法吗?

【问题讨论】:

  • 我不知道你指的光标是什么样子的,你能添加一张图片吗?
  • 如果您愿意放弃确切的索引要求,您可以在 textarea 上监听 drop 事件,调用 e.preventDefault,获取文件名,然后将该名称插入到任何您想要的位置'd like(嗯,几乎在任何地方 - 除了确切的光标索引:))。

标签: javascript google-chrome drag-and-drop


【解决方案1】:

唷,你想做的事情并不容易,因为没有办法引用这个特定的插入符号!

在我的脑海中,您只能通过繁重的变通方法来实现这一点:在drop 发生时您可以获得的是鼠标光标位置。

您必须制作一个与 textarea 在形状、文本大小、边距等方面相同的不可见 div 克隆,以便自动填充来自您的 textarea 的文本。

接下来,您必须在此 div 中为每个可能的插入符号位置(即文本的每个字符 1 个跨度)创建一个跨度,并获取每个跨度的 x 和 y 坐标并将它们保存到一个数组中。

.txtarea {
  width: 300px;
  height: 150px;
  font-size: 12px;
  font-family: Arial;
  padding: 5px;
  text-align: left;
  border: 1px solid red;
}
<textarea class="txtarea">Mytext</textarea>

<!--just an example, auto-fill this content through a JS oninput event -->
<div class="txtarea"><span>M</span><span>y</span><span>t</span><span>e</span><span>x</span><span>t</span></div>

然后,在发生drop 时,获取事件的鼠标坐标,将它们与坐标数组approximate the closest x/y position 进行比较,并在此基础上在您的textarea 中设置一个新的selectionStart 索引,插入文件名,然后恢复之前的选择开始。

【讨论】:

    【解决方案2】:

    这只是部分答案:以下适用于 IE11,但不适用于 Chrome(未在其他浏览器中测试)。

    只需从上方input中选择一些字母并将它们拖到底部:

    let to = document.getElementById("to");
    to.addEventListener("dragover", function (e) { 
      if (e.target.selectionStart) { 
         console.log(e.target.selectionStart); 
      }
    });
    <input type="text" id="from" value="select some letters from this sentence, and drag them into the other input element"/ style="width: 500px;"><br/>
    <input type="text" id="to" value="drag over here"/>

    未来研究的几点说明:

    • 在 Chrome 中,dragover 事件仅在 input 元素内首次设置焦点后触发。

    • 在 Chrome 中,selectionStart 的值是目标输入中最后选择的文本位置的值(换一种说法:光标在目标输入中的最后位置)。

    • 在 Chrome 中设置 type="number" 会触发 selectionStart 分配给 null,但在 IE11 中不会。

    【讨论】:

    • 感谢您的回答!实际上我的问题是关于从操作系统文件系统中拖动文件;这种行为与从同一网页拖动内容完全不同。
    • 不客气,抱歉我帮不上忙。请注意,dragover 的行为实际上是相同的,即:在 IE11 中,您将获得正确的 selectionStart 值,而在 Chrome 中,selectionStart 将具有目标输入中最后选择的文本位置的值(即,文件被拖入的位置),假设它在拖动之前获得焦点。
    【解决方案3】:

    从你的图片来看,它看起来像是一个输入文本(我承认,我懒得为 textarea... 编写代码)但是 - 无论如何 - 这是一个很好的问题。

    要获取文本宽度,您需要克隆原始元素并逐个字符循环遍历文本。仅举一个例子,请在此处查看有关该主题的一些答案:“如何使用 JavaScript 设置 省略号 文本”。

    我还注意到,通过将鼠标指针移到文本上,插入符号会从每个字符的中心左右移动。最后,这里的主要问题是:如何找到每个char中间的x

    所以,我的代码分为两部分:文本输入克隆字符位置。之后,要定位插入符号,您可以使用这样的现有库:jQuery Caret PluginjQuery Caret - 由您决定 - 我会跳过这部分问题。

    我在 FF、Safari、IE 和 Chrome 中测试了下面的代码,这些浏览器的拖放行为显然存在一些小而烦人的问题,但在我看来它们无关紧要。

    function getCaretPos(target, x) {
      var txt = target.value, doc = target.ownerDocument,
        clone = doc.createElement('span'), /* Make a clone */
        style = doc.defaultView.getComputedStyle(target, null),
        mL = style.getPropertyValue('margin-left'),
        pL = style.getPropertyValue('padding-left'),
        mouseX = x - parseFloat(mL) - parseFloat(pL);
    
      clone.style.fontFamily = style.getPropertyValue('font-family');
      clone.style.fontSize = style.getPropertyValue('font-size');
      clone.style.fontWeight = style.getPropertyValue('font-weight');
      clone.style.position = 'absolute'; /* Keep layout */
      clone.style.left = -9999; 
      target.parentNode.appendChild(clone);
      clone.style.width = 'auto'; 
      clone.style.whiteSpace = 'pre'; /* Keep whitespaces */
      clone.style.marginLeft = 0;
      clone.style.paddingLeft = 0;
      
      clone.innerText = txt; /* Start from full length */
      var i = txt.length, pos = -1, xL = 0, xC = 0, xR = clone.clientWidth;
      while (i--) { /* Find the caret pos */
        clone.innerText = txt.slice(0, i);
        xL = clone.clientWidth;
        xC = (0.5 * (xR + xL)) | 0; /* We need the center */
        if (xC < mouseX) { pos = i; break }
        xR = xL;
      }
      pos++; /* Restore the correct index */
      target.parentNode.removeChild(clone); /* Clean up */
      clone = null;
      return pos;
    }
    
    function onFileDragOver(e) {
      if(!window.chrome) e.preventDefault(); /* Show the caret in Chromium */
    }
    
    function onFileDrop(e) {
      e.preventDefault();
      var target = e.currentTarget, txt = target.value,
        pos = getCaretPos(target, e.offsetX),
        tok1 = txt.substr(0, pos), tok2 = txt.substr(pos);
    
      /* Get the drop-action result */
      var result = '', files = e.dataTransfer.files,
        data = e.dataTransfer.getData('text');
    
      for (var i = 0, f; (f = files[i]); i++) { result += f.name }
      target.value = tok1 + result + tok2;
      target.focus();
      /* Up to You how to position the caret */
      if(target.setSelectionRange) target.setSelectionRange(pos, pos);
      //$(target).caret(pos); 
    }
    
    $(document).ready(function () {
      var target = document.getElementById('search-input');
      target.addEventListener('dragover', onFileDragOver);
      target.addEventListener('drop', onFileDrop);
    });
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
        <link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.2/jquery.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquerymobile/1.4.5/jquery.mobile.js"></script>
      </head>
      <body>
        <div data-role="page">
          <div data-role="header"><h1>Caret Position</h1></div>
          <div data-role="content">
            <div class="ui-field-contain">
              <label for="search-input">Products</label>
              <input type="search" name="search-input" id="search-input" value="target text box"
              spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="none"/>
            </div>
          </div>
        </div>
      </body>
    </html>

    【讨论】:

      【解决方案4】:

      试试 jQuery。您需要做的是获取带有查询选择器的文本框,然后将其绑定到 mousemove 并获取 $(this).caret().start

      例子:

      let cursorPosition = 0;
      
      $("#textinput").bind("mousemove", function() {
        cursorPosition = $(this).caret().start;
      });
      
      // Do what you want with cursorPosition when file is dropped
      

      我认为这就是你需要做的所有事情。

      只要你愿意使用 jQuery 就可以了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-26
        • 2012-04-14
        • 1970-01-01
        • 2017-10-16
        • 2017-06-09
        相关资源
        最近更新 更多