textarea调整大小的移动浏览器问题

Posted

技术标签:

【中文标题】textarea调整大小的移动浏览器问题【英文标题】:Mobile browser issue with textarea resize 【发布时间】:2019-06-24 09:08:07 【问题描述】:

我在 React.js 中工作,并且有 textarea 元素可以根据用户输入的大小动态扩展和收缩。预期功能如下:

这在桌面环境中可以正常工作。但是,在现代浏览器(经过测试的 Safari、Chrome 和 Firefox)中的任何手机或平板电脑上,textarea 元素只会扩展,不会在删除内容时收缩。

起初我认为这可能与我正在使用的onChange 处理程序有关,但是,将其换成onInput 处理程序时仍然存在同样的问题。所以我认为问题在于resize() 方法。

有人知道我为什么会遇到这个问题吗?

我创建了一个无样式的小提琴,与您分享基本功能。有趣的是,该错误不会出现在移动设备上的 JSFiddle 模拟器中,但如果您将相同的代码放在另一个 react 环境中,则该错误会出现在现代浏览器的移动设备上。

class Application extends React.Component 
  render() 
    return (
      <div>
        <Textarea value="This is a test" maxLength=500/>
      </div>
    );
  



class Textarea extends React.Component 

  constructor(props) 
    super(props);

    this.state = 
      value: this.props.value
        ? this.props.maxLength && this.props.maxLength > 0
          ? this.props.value.length < this.props.maxLength
            ? this.props.value
            : this.props.value.substring(0, this.props.maxLength)
          : this.props.value
        : '',
      remaining: this.props.value
        ? this.props.value.length < this.props.maxLength
          ? this.props.maxLength - this.props.value.length
          : 0
        : this.props.maxLength
    ;

    this.textAreaRef = React.createRef();
    
    this.textAreaHeight = null;
    this.textAreaoffSetHeight = null;
  
  
  
  componentDidMount() 
    window.addEventListener('resize', this.resize);
    this.resize();
  
  
  componentWillUnmount() 
    window.removeEventListener('resize', this.resize);
  
  
  handleChange = event => 
    const target = event.target || event.srcElement;

    this.setState(
      value: target.value,
      remaining: target.value
        ? target.value.length < this.props.maxLength
          ? this.props.maxLength - target.value.length
          : 0
        : this.props.maxLength
    );

    this.resize();
  ;
  
  resize = () => 
    const node = this.textAreaRef.current;

    node.style.height = '';

    const style = window.getComputedStyle(node, null);

    let heightOffset =
      parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);

    this.textAreaoffSetHeight = node.offsetTop;

    this.textAreaHeight = node.scrollHeight + heightOffset;

    node.style.height = this.textAreaHeight + 'px';

    this.resizeBorder();
    this.resizeParentNode();
  ;

  resizeBorder = () => 
    const textAreaSize = this.textAreaHeight;
    const node = this.textAreaRef.current;
    const borderNode = node.parentNode.querySelector(
      '.textarea__border'
    );
    
    if (borderNode !== null) 
      borderNode.style.top =
        this.textAreaoffSetHeight + textAreaSize - 1 + 'px';
    
  ;

  resizeParentNode = () => 
    const node = this.textAreaRef.current;
    const parentNode = node.parentNode;
    
    if (parentNode !== null) 
      parentNode.style.height = this.textAreaHeight + 40 + 'px';
    
  ;

    render() 
    return (
      <div className='textarea'>
        <textarea
          ref=this.textAreaRef
          className=
            !this.state.value
              ? 'textarea__input'
              : 'textarea__input active'
          
          value=this.state.value
          maxLength=
            this.props.maxLength && this.props.maxLength > 0 ? this.props.maxLength : null
          
          onChange=this.handleChange
        />
        <div className='textarea__message'>
            this.state.remaining <= 0
              ? `You've reached $this.props.maxLength characters`
              : `$this.state.remaining characters remaining`
          </div>
      </div>
    );
    



ReactDOM.render(
  <Application />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main id="app">
    <!-- This element's contents will be replaced with your component. -->
</main>

【问题讨论】:

我现在添加了更合适的小提琴,以及合理的赏金 您提供的 jsfiddle 似乎过于复杂。这是我的 GitHub 存储库,你可以试试这个,看看你是否也看到相同的错误? github.com/MartinDawson/react-fluid-textarea @danMad 我已经在 React Context here 中从 JsFiddle 复制了你的代码,它似乎没有任何奇怪的行为。你能确认一下吗? 无论如何都无法重现问题 我猜在我的手机上一切正常 【参考方案1】:

问题是您正在直接(或尝试)修改 DOM,而不是修改状态并允许 React 正常流动。您修改 resize() 中的 DOM 元素属性,然后任何输入更改将立即调用 handleChange(e) 并重新排列您的 DOM 覆盖修改。

永远不要将 React 与 DOM Touching 混为一谈!!!

将您的 resize 函数更改为与您的 handleChange(e) 函数类似,并在标记的 render() 期间控制这些属性的状态内设置变量。

【讨论】:

我喜欢你说的:永远不要与 DOM TOUCHING 混合反应!!!

以上是关于textarea调整大小的移动浏览器问题的主要内容,如果未能解决你的问题,请参考以下文章

当浏览器调整大小时,不断在视频上移动可调整大小/可拖动的图像

JavaScript:调整笔记本电脑的浏览器窗口大小(“移动” - “桌面” - “移动”)不会重启菜单状态

如何确定移动浏览器中的软键盘是不是触发了调整大小事件?

jQuery - 调整浏览器大小时移动 div

移动端rem布局,用户调整手机字体大小或浏览器字体大小后导致页面布局出错问题

在一个 WordPress 主题上调整浏览器窗口的大小以及添加 JS 代码移动菜单关闭按钮后,导航栏不起作用