检测 flex-wrap 影响的元素

Detect which element flex-wrap affects

我正在制作一个类型测试应用程序,其中有一个插入符号行“|”从左向右移动。

这是我游戏的粗略构建:

.words_wrapper {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  width: 400px;
}

.word {
  padding: 0.2rem;
}

.game{
  position: relative;
}
.caret {
  position: absolute;
  top: 0;
  height: 22px;
  width: 2px;
  background-color: red;
}
<div class="game">
  <div class="caret" style="left: 10px;"></div>
  <div class="words_wrapper">
    <div class="word">bob</div>
    <div class="word">about</div>
    <div class="word">us</div>
    <div class="word">know</div>
    <div class="word">computer</div>
    <div class="word">again</div>
    <div class="word">fails</div>
    <div class="word">running</div>
    <div class="word">bob</div>
    <div class="word">about</div>
    <div class="word">us</div>
    <div class="word">know</div>
    <div class="word">computer</div>
    <div class="word">again</div>
    <div class="word">fails</div>
    <div class="word">running</div>
  </div>
</div>

我有一个状态变量来调整属性

const [caretPos,setCaretPos] = useState();

然后我在打字的时候根据其他因素修改这个位置,应用在div样式中。

<div className='caret' style={{left: `${caretPos}px`}>

现在,我的问题是,如何检测插入符是否在行尾并使其转到下一行

请注意 div 有一个 flex-wrap 属性 所以它会自动让单词转到下一行。有没有办法检测哪个单词在行尾或行首?

这个问题我有两种思路:

  • 第一个直接回答你的代码问题(我如何检测下一个 flex 元素何时换行)。
  • 第二种方法用另一种方法解决了您的光标问题。

正在检测 Flex Wrap

您可以通过 getBoundingClientRect 使用对象的实际位置来检测环绕。此函数为您提供相对于视口的位置。至关重要的是,如果元素的垂直位置与前一个元素的垂直位置不同,我们就知道该元素会“环绕”。

const isWrapping = (previous, current) =>
  previous.getBoundingClientRect().top !== current.getBoundingClientRect().top

const isWrapping = (previous, current) =>
  previous.getBoundingClientRect().top !== current.getBoundingClientRect().top

const highlightWrappingElements = (container) => {
  for (let i = 1; i < container.children.length; ++i) {
    const previous = container.children[i - 1]
    const current = container.children[i]
    
    if (isWrapping(previous, current))
      current.classList.add('wrapping')
  }
}

highlightWrappingElements(document.querySelector('#container'))
.container {
  width: 30rem;
  border: 1px solid black;
  gap: 0.5rem;
  display: flex;
  flex-wrap: wrap;
}

.box {
  width: 5rem;
  height: 5rem;
  background: blue;
}

.wide { width: 7.5rem; }
.wrapping { background: orange; }
<div id="container" class="container">
  <div class="box wide"></div>
  <div class="box"></div>
  <div class="box wide"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box wide"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box wide"></div>
  <div class="box wide"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>

在示例中,开始新行的元素是橙色的。

如何将此技术集成到您的问题中取决于您的 React 代码的设置方式,但这个想法应该是一个很好的起点。

移动插入符号[=​​54=]

您的实际目标是在一段字母中移动插入符号。要准确地做到这一点,它必须:

  • 知道每个字母的长度以准确定位自己
  • 知道行尾在哪里,以便它可以换行

这些是 non-trivial 问题,尤其是当框必须调整大小或段落内容是动态的时。也就是说,如果您尝试使用 position: absolute.

手动定位插入符号

相反,您实际上可以在文本中嵌入插入符号。

<p>This is s<span class="caret"></span>ome text.</p>

这立即解决了上述两个问题,因为只要您在插入符号的位置发生变化时重新渲染节点,插入符号就会始终准确地位于它需要的位置。

警告: 在 Chrome 上,插入符跨度会打断导致它在行尾换行的单词。这可以通过例如将 span 中的每个单词包装为 display: inline-block 来解决。为了简洁起见,我在下面的代码中省略了如何执行此操作。

const paragraph = document.querySelector('#paragraph')
const content = paragraph.textContent

const caret = document.createElement('span')
caret.classList.add('caret')
let caretPosition = 0
const advanceCaret = () => {
  caretPosition += 1
  if (caretPosition >= content.length)
    caretPosition = 0

  paragraph.innerHTML = content.substring(0, caretPosition) + caret.outerHTML + content.substring(caretPosition)
}

setInterval(advanceCaret, 100)
.container {
  width: 30rem;
  border: 1px solid black;
  padding: 1rem;
  font-size: 1.25rem;
}

p { margin: 0; }

.caret {
  position: relative;
  overflow: visible;
}

.caret::before {
  content: '';
  border-left: 2px solid red;
  position: absolute;
  height: 100%;
  top: 0;
  left: 0;
}
<div class="container">
  <p id="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

我更喜欢这个,因为它很简单并且避免了手动定位的大部分复杂性,但它是否可行取决于您最终如何呈现您的文本段落。