reactjs 在给定元素上调用焦点后,选择范围的getClientRect将返回一个空列表

xyhw6mcr  于 2023-02-04  发布在  React
关注(0)|答案(1)|浏览(108)

我使用以下代码来选择元素

const selectEnd = (element: HTMLElement, position: number) => {
  element.focus();
  
  const range = document.createRange();
  const sel = window.getSelection();

  if (position !== -1) {
    range.setStart(element, Math.min(position, element.childNodes.length));
  } else {
    range.setStart(element, element.childNodes.length);
  }

  sel?.removeAllRanges();
  sel?.addRange(range);
};

选择元素后,以下rect变量将始终处于未定义状态

const selection = window.getSelection();

if (!selection) return;

const range = selection.getRangeAt(0).cloneRange();
range.collapse(true);

const rect = range.getClientRects()[0];

但是在客户端发生onInput事件之后,range.getClientRects函数将返回一个值。
我尝试让range.getClientRects()在focus调用后返回一个变量,而不需要用户输入任何内容。

ghg1uchk

ghg1uchk1#

你在CSSWG#2514上感觉到了。
问题是浏览器无法计算出DOMRects组成的折叠Range锚定到Element(而不是Node)。
本文CKEditor issue列出了可能发生这种情况的几种情况,以及一些潜在的解决方法。

      1. Element中的折叠Range具有内容。(很可能是您的情况)**

在最近的TextNode上设置Range将允许浏览器正确地计算框:

const el = document.querySelector("div");
const range = new Range();
// to show the "bug"
range.selectNode(el);
range.collapse(true);
console.log("selecting the Element", range.getClientRects().length); // 0

// the workaround
range.setStart(el.firstChild, 0);
range.collapse(true);
console.log("selecting the Node", range.getClientRects().length); // 1
<div>target</div>

尽管元素的firstChild也可能不是TextNode,所以实际上需要一个方法来检索Element中的第一个TextNode
一个二个一个一个

    • 2.空Element中的Range(因此没有TextNode可供选择)**

对于这个示例,您可以尝试在Element中附加一个零宽度空格(ZWSP)字符,在新创建的TextNode上进行测量,然后删除该字符。

function getFirstTextNode(target) {
  return document.createNodeIterator(target, NodeFilter.SHOW_TEXT).nextNode();  
}
const el = document.querySelector("div");
const range = new Range();
let firstTextNode = getFirstTextNode(el);
const wasEmpty = !firstTextNode;

if (wasEmpty) {
  console.log("inserting a ZWSP");
  firstTextNode = new Text("\u200B");
  el.append(firstTextNode);
}
range.setStart(firstTextNode, 0);
range.collapse(true);
const rect = range.getClientRects()[0];
if (wasEmpty) {
  firstTextNode.remove();
}
console.log(rect); // DOMRect
<div></div>
    • 3.位于两个元素之间的Range**
...</div>|<div>...

这里你可以再一次使用ZWSP技巧。检测这个可能会变得更简洁一些,并且OP显然想在Element的开头设置插入符号,这对他们来说似乎是不可能的。所以我将离开这个例子。

相关问题