在 Chrome 中将文件拖放到文本区域时获取光标位置

Get cursor position when a file is dropped in textarea in Chrome

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

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

(Chrome 的默认行为是打开拖放的文件,但我在 drop 事件中覆盖了这个行为。我想在文本区域中插入文件名.)

我试图在 drop 发生时获得此位置(根据文本区域值字符串中的索引)。有什么想法吗?

这只是部分答案:以下在 IE11 中有效,但在 Chrome 中无效(未在其他浏览器中测试)。

只是 select 上面 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 中,只有在 input 元素内首次设置焦点后才会触发 dragover 事件。

  • 在Chrome中,selectionStart的值是目标输入中最后selected文本位置的值(换句话说:最后位置目标输入内的光标)。

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

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

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

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

接下来,您必须在 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 并在基于文本的文本区域中设置一个新的 selectionStart 索引在那上面,插入文件名,然后恢复之前的选择开始。

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

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

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

因此,我的代码分为两部分:文本输入克隆字符位置。之后,要定位插入符号,您可以使用这样的现有库:jQuery Caret Plugin or jQuery 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>

尝试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就可以了