zoukankan      html  css  js  c++  java
  • 高度自适应的输入框

    有时候我们需要一个高度能随内容自动增加的输入框,input 显然不行,因为 input 里的文字是不换行的。文本域 textarea 里的文字倒是换行的,可一旦文字内容超过其高度,textarea 就会增加一个烦人的滚动条,这是很影响视觉的,就如同下面:

    <textarea cols="30" rows="3"></textarea>
    

      那么有没有办法制作一个高度能随文字内容自动增加的输入框呢?答案是肯定的,下面介绍两种方式。

    方式一

      这种方式依然使用 textarea, 主要思想是我们将 textarea 放入一个容器中,同时在这个容器中放入一个隐藏的 div (visibility: hidden), 监听 textarea 的输入事件并将其中的文字动态的同步到隐藏的div中,这样div 就可以撑开容器,这时设置 textarea 的高度为 100% 并将其定位到容器的左上角,那么 textarea 的高度自然就是其中文字内容的高度了。

    visibility 是一个CSS属性,用来在不更改文档的布局的前提下显示或隐藏元素,它有三个可能的取值:

    1. visible 元素正常显示(默认值);
    2. hidden 隐藏元素,但是其他元素的布局不改变,相当于此元素变成透明。
        若将其子元素设为 visibility: visible,则该子元素依然可见;
    3. collapse 用于表格的行、列、列组和行组,隐藏表格的行或列,并且不占用任何空间。
       <!-- demo-1.html -->
    
       <div class="container">
          <div></div>
          <textarea placeholder="输入消息..."></textarea> 
       </div>
       
       <style>
          .container {
              500px;
             position: relative;
          }
          .container div {         
             visibility: hidden;
             /** 避免初始化时容器没有高度 */
             padding: 8px 0px;
          }
          .container textarea {
              100%;
             height: 100%;
             position: absolute;
             top: 0px;
             padding: 0px;
             /** 必须设置为 content-box !!! */ 
             box-sizing: content-box;
          }
       </style>
    
       <script>
          const textarea = document.querySelector('.container textarea');
          const div = document.querySelector('.container div');
          textarea.addEventListener('input', (e) => {
             div.innerText = e.target.value;
          });
       </script>
    

    查看样例:https://mxsyx.site/archives/10/#demo-1

      你可能已经注意到了,当我们输入文字时,输入框的高度显然要比文字内容高许多,伴随着输入文字的增多。高度差会越来越大,这是因为隐藏 div 与 文本域 textarea 内字体的尺寸与行高是不同的, div 内的字体尺寸与行高要比 textarea 内的大,所以 div 撑开的容器高度自然要高于 textarea 内的文字内容高度。要解决这个问题,统一它们的字体尺寸与行高就可以了。(注:div 的字体尺寸与行高默认继承自父元素)

       <!-- demo-1.html -->
    
       <div class="container">
          <div></div>
          <textarea placeholder="输入消息..."></textarea> 
       </div>
    
       <style>
          .container {
              500px;
             position: relative;
             font-size: 14px;
             line-height: 16px;
          }
          .container div {
             visibility: hidden;
             /** 避免初始化时容器没有高度 */
             padding: 8px 0px;
          }
          .container textarea {
              100%;
             height: 100%;
             position: absolute;
             top: 0px;
             padding: 0px;
             /** 必须设置为 content-box !!! */
             box-sizing: content-box;
             /** 设置字体尺寸与行高继承自父元素 */
             font-size: inherit;
             line-height: inherit;
             /* 去掉右下角的调整大小的标志 */
             resize: none;
          }
       </style>
       <script>
          const textarea = document.querySelector('.container textarea');
          const div = document.querySelector('.container div');
          textarea.addEventListener('input', (e) => {
             div.innerText = e.target.value;
          });
       </script>
    

    查看样例:https://mxsyx.site/archives/10/#demo-2

      这样一来高度就一致了。这种方式虽然可以较好的实现高度自适应的输入框,但实现起来总感觉很粗糙,下面这种方式就明显简单多了。

    方式二

      像 div, p, blockquote 这样的元素默认是不可编辑的,但我们可以将其 contenteditable 属性设置为 true, 使其变为可编辑的。

    contenteditable是一个全局属性,用于指示元素是否可被用户编辑,该属性必须采用以下值之一:

    1. true 或者 '空字符串', 表示该元素是可编辑的;
    2. false, 表示该元素是不可编辑的。
    3. 如果未设置此属性,则其默认值将从其父元素继承。
      <div class="container" contenteditable="true"></div>
    
      <style>
        .container {
           500px;
          font-size: 14px;
          line-height: 16px;
          border: solid 1px #999;
        }
      </style>
    

    尝试输入一段文字吧:

      是不是很简单呢? 我们也可以使用CSS伪类 :empty, :focus, 实现placeholder 那样的效果

    <style>
    .container {
       500px;
      font-size: 14px;
      line-height: 16px;
      border: solid 1px #999;
    }
    .container:empty::before {
      content: "输入消息...";
      color: #999999;
    }
    .container:focus::before {
      content: none;
    }
    </style>
    

    尝试输入一段文字吧:

      如果你使用 Vue.js, 我们也可以它封装为一个Vue组件:

    <template>
        <div
          class="msg-input"
          contenteditable="true"
          @input="changeText"
        >{{ innerText }}</div>
    </template>
    
    <script>
    export default{
      name: "MsgInput",
      props: ['value'],
      
      data: function() {
        return {
          innerText: this.value,
        }
      },
      
      methods: {
        changeText() {
          this.$emit('input', this.$el.innerText);
        }
      }
    }
    </script>
    
    <style scoped>
    .msg-input {
       500px;
      font-size: 14px;
      line-height: 16px;
      border: solid 1px #999;
    }
    .msg-input:empty::before {
      content: "输入消息...";
      color: #999999;
    }
    .msg-input:focus::before {
      content: none;
    }
    </style>
    

    接下来在父组件中引用这个组件:

    <template>
      <div>
        <MsgInput v-model="msg"/>
      </div>
    </template>
    
    <script>
    import MsgInput from "/MsgInput.vue";
    
    export default {
      name: "MsgToolkit",
      data: function() {
        return {
          msg: ''
        }
      },
    
      components: {
        MsgInput
      },
    }
    </script>
    

      父组件为子组件使用 v-model 指令,将子组件的 value 与 父组件的 msg 双向绑定在一起。当输入事件发生后,子组件调用changeText方法,触发一个 input 事件,父组件监听到此事件,将事件传递过来的数据同步到 msg 上,由于数据是双向绑定的,子组件的 value 值也会相应发生变化。更过原理请参考 自定义组件的-v-model

    该篇博客内的代码已同步到Github

    参考资料:
    [1]. MDN文档 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/contenteditable
    [2]. MDN文档 https://developer.mozilla.org/zh-CN/docs/Web/CSS/visibility
    [3]. MDN文档 https://developer.mozilla.org/zh-CN/docs/Web/CSS/:empty
    [4]. MDN文档 https://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus
    [5]. Vue.js官方文档 https://cn.vuejs.org/v2/guide/components-custom-events.html#自定义组件的-v-model

  • 相关阅读:
    ubuntu19.04 redis启动和停止及连接
    ubuntu Redis安装及配置
    Django 基本使用及目录结构
    selenium设置chrome请求头
    多进程爬虫python——实例爬取酷狗歌单
    2020第一周学习记录
    爬取博客园最新文章
    python下用selenium的webdriver包如何在执行完点击下一页后没有获得下一页新打开页面的html源代码
    质量属性六个常见属性《钉钉》场景分析
    warnings.warn('Selenium support for PhantomJS has been deprecated, please use headless '报错
  • 原文地址:https://www.cnblogs.com/nkqlhqc/p/12035350.html
Copyright © 2011-2022 走看看