javascript - 可以在没有持续回流的情况下动态调整高度的文本区域吗?

标签 javascript html css reactjs reflow

注意:据我所知,这不是重复项,因为使用 contentEditable div 似乎不是一个好的选择。它有很多问题(没有占位符文本,需要使用 dangerouslySetInnerHTML hack 来更新文本,选择光标很挑剔,其他浏览器问题等)我想使用文本区域。

我目前正在为我的 React textarea 组件做一些事情:

componentDidUpdate() {
  let target = this.textBoxRef.current;

  target.style.height = 'inherit';
  target.style.height = `${target.scrollHeight + 1}px`; 
}

这有效并允许文本区域在添加和删除换行符时动态增加和缩小高度。

问题是每次文本更改时都会发生重排。这会导致应用程序出现大量滞后。如果我在文本区域中按住一个键,则会在附加字符时出现延迟和滞后。

如果我删除 target.style.height = 'inherit'; 行,延迟就会消失,所以我知道这是由这种持续回流引起的。

我听说设置 overflow-y: hidden 可能会摆脱不断的回流,但在我的情况下却没有。同样,设置 target.style.height = 'auto'; 不允许动态调整大小。

我目前已经开发了一个解决方案来解决这个问题,但我不喜欢它,因为它是每次文本更改时的 O(n) 操作。我只是计算换行符的数量并相应地设置大小,如下所示:

// In a React Component

handleMessageChange = e => { 
  let breakCount = e.target.value.split("\n").length - 1;

  this.setState({ breakCount: breakCount });
}

render() {
  let style = { height: (41 + (this.state.breakCount * 21)) + "px" };

  return (
    <textarea onChange={this.handleMessageChange} style={style}></textarea>
  );
}

最佳答案

我认为 thirddot 的推荐可能是最好的。 Material UI textarea他链接有一个非常聪明的解决方案。

他们创建了一个隐藏的绝对定位的文本区域,模仿实际文本区域的样式和宽度。然后他们将您键入的文本插入该文本区域并检索它的高度。因为它是绝对定位的,所以没有回流计算。然后他们将该高度用作实际文本区域的高度。

我不完全理解他们的代码在做什么,但我已经针对我的需要进行了最少的重新调整,而且它似乎运行良好。以下是一些片段:

.shadow-textarea {
  visibility: hidden;
  position: absolute;
  overflow: hidden;
  height: 0;
  top: 0;
  left: 0
}
<textarea ref={this.chatTextBoxRef} style={{ height: this.state.heightInPx + "px" }}
          onChange={this.handleMessageChange} value={this.props.value}>
</textarea>

<textarea ref={this.shadowTextBoxRef} className="shadow-textarea" />
componentDidUpdate() {
  this.autoSize();
}

componentDidMount() {
  this.autoSize();
}
autoSize = () => {
  let computedStyle = window.getComputedStyle(this.chatTextBoxRef.current); // this is fine apparently..?

  this.shadowTextBoxRef.current.style.width = computedStyle.width; // apparently width retrievals are fine
  this.shadowTextBoxRef.current.value = this.chatTextBoxRef.current.value || 'x';

  let innerHeight = this.shadowTextBoxRef.current.scrollHeight; // avoiding reflow because we are retrieving the height from the absolutely positioned shadow clone

  if (this.state.heightInPx !== innerHeight) { // avoids infinite recursive loop
    this.setState({ heightInPx: innerHeight });
  }
}

有点hacky,但它似乎工作得很好。如果有人可以很好地改进它或用更优雅的方法清理它,我会接受他们的回答。但这似乎是考虑到 Material UI 使用它的最佳方法,而且它是迄今为止我尝试过的唯一一种消除了在足够复杂的应用程序中导致延迟的昂贵回流计算的方法。

Chrome 仅在高度变化时报告回流发生一次,而不是在每次按键时发生。因此,当文本区域增大或缩小时仍然存在 30 毫秒的滞后,但这比每次击键或文本更改都要好得多。通过这种方法,延迟消失了 99%。

关于javascript - 可以在没有持续回流的情况下动态调整高度的文本区域吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57965268/

相关文章:

php - CodeIgniter 相对路径或基本 URL 函数?

javascript - 当值具有 HTML 字符时,按其值选择元素

javascript - AngularJS : ng-bind-html depending on variable

javascript - 任何人都知道如何使用 jquery 在同一行中制作相等的 div 高度

css - 为什么 IE7 和 IE8 会忽略我在 wordpress 中对 .widget 类的编码?

javascript - 如何从spring Controller 方法更新html img src?

Javascript 转换,从 Prototype 到 jQuery

html - 字体大小 CSS 问题

javascript - 使用 JavaScript 更改 IE 中的背景图像

css - @font-face 仅在 www.不在地址。只有域名,正确显示