textarea自适应高度二——(设置隐藏div获取高度和仿element-ui组件)

news2024/11/16 20:52:37

文章目录

  • 前言
  • 一、通过隐藏div的方式来设置文本域自适应高度
    • 1. 新增一个文本域样式一个的dom,但是里面的textarea改为div
    • 2. 隐藏div的class
    • 3.设置文本域高度的方法
  • 二、仿element-ui组件设置textarea自适应高度
    • 1.element-ui中自适应效果
    • 2. 看源码,盘逻辑,找思路
      • (1)找到element-ui的input
      • (2)在input.vue中查看autosize 方法(element-ui就是由这个方法控制文本域高度的)
      • (3)resizeTextarea方法
      • (3)resizeTextarea触发时机(文件中搜索只有四次)
    • 3. 项目中实际使用及其代码
      • (1) calcTextareaHeight.js
      • (2) 文本域组件 new-textarea.vue (名字任意,别取textarea)
      • (3) 要使用文本域高度自适应的页面(例如index.vue)
      • (4)效果
  • 总结


前言

css、js(vue)进行textarea自适应高度(超详细说明) 一文中我有使用弹性布局来设置样式,通过监听文本域的input 事件修改当前文本域的高度来达到 文本域自适应高度的问题
关键代码如下:
在这里插入图片描述

其实上面的已经可以解决大部分关于文本域自适应高度的问题了,但当我需求是默认显示一行,最高显示4行的时候,出现了一个新问题。在字体大小为16px,行高为30px的情况下,当你设置this.style.height = 'inherit'的时候,行高默认展示的 scrollHeight 会加上padding的值,这时候我的 padding 为上下16px,这就间接导致初始化文本域高度的时候,所展示的默认高为 32 (行高) + 32 (上下的padding) = 64, 当我不改变设置this.style.height = 'inherit'的时候,而文本域由于有placeholder导致无法最小显示两行,这属于一个无法修改的bug。
在这里插入图片描述

关于w3c上scrollHeight 的定义:
scrollHeight 属性是一个只读属性,它返回该元素的像素高度,高度包含内边距(padding),不包含外边距(margin)、边框(border),是一个整数,单位是像素 px。


一、通过隐藏div的方式来设置文本域自适应高度

原理:复制一份展示文本域的内容,然后将这个div设置z-index为100(数值任意,但是不能在页面上看到这个div,直接设置display: none会导致获取不到隐藏div的高度)。将这个隐藏div的scrollHeight赋值给文本域即可。

1. 新增一个文本域样式一个的dom,但是里面的textarea改为div

<div class="textarea_box">
  <textarea v-model="text" key="teatareaId" id="teatareaId" class="textarea" placeholder="请输入内容"></textarea>
  <div class="btn"></div>
</div>
<div class="textarea_box">
  <div key="teatareaDivId" id="teatareaDivId" class="textarea_div">{{text}}</div>
  <div class="btn"></div>
</div>

2. 隐藏div的class

直接设置display: none会导致获取不到隐藏div的高度,所以这里使用了z-index: 100,用层级将其遮挡。

.side {
 display: flex;
  flex-direction: column;
  height: 100%;
  .textarea_box {
    display: flex;
    width: 100%;
    border-radius: 4px;
    border: 1px solid #e5e9eb;
    background: #fff;
    box-sizing: border-box;
    .textarea{
      flex: 1;
      height: 32px;
      line-height: 30px;
      font-size: 16px;
      margin: 16px;
      max-height: 122px;
      overflow-x: hidden;
      overflow-y: auto;
      box-sizing: border-box;
    }
    .textarea_div{
      position: absolute;
      z-index: -100;
      width: calc(100% - 128px);
      min-height: 32px;
      line-height: 30px;
      font-size: 16px;
      margin: 16px;
      box-sizing: border-box;
      white-space: pre-wrap;
    }
    .btn{
      position: relative;
      width: 48px;
      height: 100%;
      border-radius: 0 3px 3px 0;
      background: #1879fe;
      &_disabled{
        opacity: 0.3;
      }
    }
  }
}

3.设置文本域高度的方法

// 设置文本域高度的方法
setTextareaHeight() {
  this.$nextTick(function () {
    let teatareaDom = document.getElementById('teatareaId'); // 文本域的dom
    let teatareaDivDom = document.getElementById('teatareaDivId'); // 隐藏input的dom
    function setHiehgt(){
      chapDom.style.height = "auto"; // 初始化高度
      chapDom.style.height = `${teatareaDivDom.scrollHeight}px`; // 文本域的高度等于隐藏input的高度
    }
    setHiehgt(); // 进入页面默认调用一次获取对应的高度
    // 监听修改了文本域的值修改高度
    document.getElementById('chapTextareaId').addEventListener("input", function() {
      setHiehgt();
    });
  });
},

执行以上步骤后,你可以根据max-height来设置最多展示几行。但是这样依旧会有一个问题, 当前按回车的时候,div里面的数据是不会换行的,在我思考N久后,找到了element-ui的input组件可以设置自适应高度

二、仿element-ui组件设置textarea自适应高度

1.element-ui中自适应效果

效果图地址: https://element.eleme.cn/#/zh-CN/component/input

效果图前:
在这里插入图片描述
效果图后(按回车):
在这里插入图片描述

2. 看源码,盘逻辑,找思路

(1)找到element-ui的input

element-ui的input地址为:https://github.com/ElemeFE/element/blob/dev/packages/input/src/input.vue。如下图:

在这里插入图片描述

(2)在input.vue中查看autosize 方法(element-ui就是由这个方法控制文本域高度的)

在这里插入图片描述

(3)resizeTextarea方法

resizeTextarea方法中,calcTextareaHeight方法是通过外部应用过来的,这个说起来比较麻烦,后面会贴代码,有兴趣的可以研究一下。然后其他的我们都了解怎么用了。最后就是看一下这个方法在哪里调用了,即触发情况。

(3)resizeTextarea触发时机(文件中搜索只有四次)

①毫无疑问:定义也是一次
②mounted中调用
在这里插入图片描述
③watch监听文本的值变动后调用
在这里插入图片描述

特别注意:里面的this.$nextTick不要遗漏。

3. 项目中实际使用及其代码

(1) calcTextareaHeight.js

文件路径: /src/utils/calcTextareaHeight.js
这里的calcTextareaHeight.js文件和element-ui里面的没有丝毫改动

在这里插入图片描述

let hiddenTextarea;

const HIDDEN_STYLE = `
  height:0 !important;
  visibility:hidden !important;
  overflow:hidden !important;
  position:absolute !important;
  z-index:-1000 !important;
  top:0 !important;
  right:0 !important
`;

const CONTEXT_STYLE = [
  'letter-spacing',
  'line-height',
  'padding-top',
  'padding-bottom',
  'font-family',
  'font-weight',
  'font-size',
  'text-rendering',
  'text-transform',
  'width',
  'text-indent',
  'padding-left',
  'padding-right',
  'border-width',
  'box-sizing'
];

function calculateNodeStyling(targetElement) {
  const style = window.getComputedStyle(targetElement);

  const boxSizing = style.getPropertyValue('box-sizing');

  const paddingSize = (
    parseFloat(style.getPropertyValue('padding-bottom')) +
    parseFloat(style.getPropertyValue('padding-top'))
  );

  const borderSize = (
    parseFloat(style.getPropertyValue('border-bottom-width')) +
    parseFloat(style.getPropertyValue('border-top-width'))
  );

  const contextStyle = CONTEXT_STYLE
    .map(name => `${name}:${style.getPropertyValue(name)}`)
    .join(';');

  return { contextStyle, paddingSize, borderSize, boxSizing };
}

export default function calcTextareaHeight(
  targetElement,
  minRows = 1,
  maxRows = null
) {
  if (!hiddenTextarea) {
    hiddenTextarea = document.createElement('textarea');
    document.body.appendChild(hiddenTextarea);
  }

  let {
    paddingSize,
    borderSize,
    boxSizing,
    contextStyle
  } = calculateNodeStyling(targetElement);

  hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
  hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';

  let height = hiddenTextarea.scrollHeight;
  const result = {};

  if (boxSizing === 'border-box') {
    height = height + borderSize;
  } else if (boxSizing === 'content-box') {
    height = height - paddingSize;
  }

  hiddenTextarea.value = '';
  let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

  if (minRows !== null) {
    let minHeight = singleRowHeight * minRows;
    if (boxSizing === 'border-box') {
      minHeight = minHeight + paddingSize + borderSize;
    }
    height = Math.max(minHeight, height);
    result.minHeight = `${ minHeight }px`;
  }
  if (maxRows !== null) {
    let maxHeight = singleRowHeight * maxRows;
    if (boxSizing === 'border-box') {
      maxHeight = maxHeight + paddingSize + borderSize;
    }
    height = Math.min(maxHeight, height);
  }
  result.height = `${ height }px`;
  hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
  hiddenTextarea = null;
  return result;
};

(2) 文本域组件 new-textarea.vue (名字任意,别取textarea)

文件路径: /src/components/new-textarea.vue

<template>
  <textarea 
  :style="textareaCalcStyle"
  v-model="nValue" 
  key="chapTextarea" 
  id="textareaId" 
  class="textarea" 
  placeholder="请输入内容" 
  wrap="hard"></textarea>
</template>

<script>
import calcTextareaHeight from '../utils/calcTextareaHeight.js'
export default {
    props: {
        value: {
            type: String,
            default: ''
        },
        maxRows: {
            type: Number,
            default: 4,  // 可以根据实际情况设置,也可以设置max-height来做限制
        }
    },
    data () {
        return {
            nValue: this.value,
            textareaCalcStyle: {}
        }
    },
    watch: {
        value: {
            handler (v) {
                if (this.nValue !== v) {
                    this.nValue = v;
                }
                this.$nextTick(() => {
                    this.resizeTextarea();
                })
            }
        },
        nValue: {
            handler (v) {
                this.$emit('input',v);
            }
        }
    },
    mounted () {
        this.$nextTick(() => {
            this.resizeTextarea();
        })
    },
    methods: {
        resizeTextarea() {
            const maxRow = this.maxRows;
            this.textareaCalcStyle = calcTextareaHeight(this.$el, 1, maxRow);
        },
    }
}
</script>

(3) 要使用文本域高度自适应的页面(例如index.vue)

文件路径: /src/views/index.vue
引入textarea.vue组件,注册,在页面上使用

// 引入
import newTextarea from '../components/new-textarea'
// 注册
export default {
  components: { newTextarea },
}
// 使用
<newTextarea v-model="textVlaue"></newTextarea>

(4)效果

①内容为空时
在这里插入图片描述
②输入回车时
在这里插入图片描述
③html中
在这里插入图片描述


总结

太难了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/728688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

病毒专题丨 plugx病毒

一、病毒简述 之前分析了一下&#xff0c;分析的较为简单&#xff0c;这次又详细分析了一下。 文件名称 00fbfaf36114d3ff9e2c43885341f1c02fade82b49d1cf451bc756d992c84b06 文件格式 RAR 文件类型(Magic) RAR archive data, v5 文件大小 157.74KB SHA256 00fbfaf36114d3ff9e…

【编程中的数学】:冰雹猜想

今天和大家分享一个令人着迷的数学谜题——冰雹猜想。这个谜题曾在1976年引起轰动&#xff0c;当时《华盛顿邮报》以头版头条刊登了一篇关于它的报道。让我们一起探索这个数学游戏的奥秘。 70年代中期&#xff0c;美国一所名牌大学的校园内兴起了一种数学游戏&#xff0c;这个游…

微信小程序使用vant时间选择器二次封装成自定义区间时间选择

目录 1.引入vant组件库 2.wxml页面 3.js页面 1.引入vant组件库 1.安装vant # 通过 npm 安装 npm i vant/weapp -S --production # 通过 yarn 安装 yarn add vant/weapp --production # 安装 0.x 版本 npm i vant-weapp -S --production 2.将 app.json 中的 "style&quo…

2-需求

目录 1.需求的定义 1.1.用户需求 1.2.软件需求 PS&#xff1a;软件需求规格说明书 2.为什么有需求&#xff1f; PS&#xff1a;为什么需求对软件测试人员如此重要&#xff1f; 3.测试人员眼里的需求 4.如何深入了解需求&#xff1f; 4.1.参加需求评审会议 4.2.查阅文…

数据结构初阶--二叉树OJ1

目录 二叉树的最大深度思路分析代码实现 相同的树思路分析代码实现 单值二叉树思路分析代码实现 二叉树的前序遍历思路分析代码实现 翻转二叉树思路分析代码实现 对称二叉树思路分析代码实现 另一棵树的子树思路分析代码实现 二叉树的最大深度 先来看题目描述 思路分析 题目…

QT学习—串口LED小项目

前言——博主刚开始接触QT&#xff0c;本文参考博主嵌入式大杂烩的一篇博文易懂 | 手把手教你编写你的第一个上位机&#xff0c;初步学习一下QT开发。 文章目录 一、QT安装二、新建工程三、创建上位机界面3.1 修改控件名3.2 添加槽函数 四、上位机程序打包五、上位机测试六、总…

不要用 in + 子查询

前两天我的 VIP 用户向我抛出了一个 SQL 问题&#xff0c;他的 MySQL 是 8.x版本&#xff1a; 大概意思如下 sql &#xff1a; select * from A where id in (select max(id) as id from A where task_id in(1,2,3) group by task_id );这个 A 表中是有 task_id 这个索引的。 …

【转换】编码转换工具笔记

应用场景 应用场景是程序整合第三方库多平台运行&#xff0c;第三方库window平台编译&#xff0c;代码移植到linux出现bom问题 思考解决 windows使用utf-8编码&#xff0c;linux使用utf-8无bom编码 工具主要针对utf-8编码文件&#xff0c;能够批量添加删除BOM&#xff0c;无…

SpringBoot获取项目日志

目的 对于布署在远端的服务&#xff0c;我们想快速的获取到日志。对于使用了日志服务&#xff0c;也可能因为上报间隔太长&#xff0c;日志不够实时。 所以想通过一些方式&#xff0c;可以不用进入到容器内也可以简单快速获取到日志&#xff0c;而且是实时的日志。目标就是获…

c语言进阶-动态内存管理

重点学习内容 动态内存管理四大函数 Malloc 内存申请函数 返回值是无类型的指针&#xff0c;指向分配的内存的首地址。申请失败会返回空指针。 malloc返回值是void*类型&#xff0c;使用时需要强制转换成所需类型。 malloc和free匹配使用&#xff0c;但是如果不free释放内存&…

解析3D视觉系统在引导金属件上下料中的应用

原创 | 文 BFT机器人 引言 Introduction 机器视觉技术作为一种高科技的智能化技术&#xff0c;正在工业生产领域发挥着越来越重要的作用。它利用计算机视觉技术&#xff0c;通过获取、处理和分析图像&#xff0c;实现对产品和工艺过程的监测、检测和控制。 随着人工智能技术的…

[SUCTF2019]hardcpp

前言 又遇到ollvm了 解混淆 可以直接用angr运行脚本去除除控制流平坦化&#xff0c;最好在ancoda等管理环境里面安装angr不然问题很多 https://github.com/Pure-T/deflat 去除前 去除后&#xff0c;它将多余的直接nop了 分析 主要加密区域位于匿名函数这一块&#xff0c…

前端学习——Web API(Day1)

Web API基本认知 Web API 基本认知 作用和分类 DOM DOM树 DOM对象 获取DOM对象 根据CSS选择器来获取DOM元素 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" …

【教学类-36-08】转学“纪念册“留念(生肖用midjounery-niji)

作品样式 背景需求&#xff1a; 即将离开班级&#xff0c;我想收集一份28个孩子的绘画册做——留念转学纪念册. 材料准备&#xff1a; 幼儿照片 ——3月初到中6班拍摄的幼儿手持学号名字纸的照片&#xff08;为了背诵幼儿信息而拍摄的照片&#xff0c;统一竖版&#xff09; 生…

jmeter 连接数据库常见报错

1. 不允许主机连接到MySQL 报错信息&#xff1a; Response message:java.sql.SQLException: Cannot create PoolableConnectionFactory (null, message from server: "Host 192.168.1.6 is not allowed to connect to this MySQL server") 说明&#xff1a;本机的地…

代码随想录day10

232. 用栈实现队列 思路&#xff1a;用两个list去模拟栈的操作&#xff0c;一个入栈list&#xff0c;一个出栈list. 并且了解栈的操作&#xff0c;pop,peek,push. 代码&#xff1a; def __init__(self):self.stack1 [] #入栈self.stack2[] #出栈def push(self, x: int) ->…

【Linux】常用网络命令:ping\netstat\mount\ifconfig

ping命令用于检测主机&#xff0c;执行ping命令指令会使用ICMP传输协议&#xff0c;发出请求回应的信息&#xff0c;若远程主机的网络功能没有问题&#xff0c;就会回应该信息。   netstat命令用于显示网络状态&#xff0c;利用netstat 指令可让你得知linux系统的网络情况。…

图像 跟踪 - MOTR: End-to-End Multiple-Object Tracking with Transformer (ECCV 2022)

MOTR: End-to-End Multiple-Object Tracking with Transformer - 使用Transformer进行端到端多目标跟踪&#xff08;ECCV 2022&#xff09; 摘要1. 引言2. 相关工作3. 方法3.1 目标检测中的查询3.2 检测查询和跟踪查询3.3 Tracklet-Aware标签分配3.4 MOTR架构3.5 查询交互模块3…

git-创建文件夹方式管理分支

文章目录 前言一、效果图二、git命令总结 前言 下面介绍一个git创建文件夹的方式管理分支的方法&#xff0c;在sourcetree上显示目录样式&#xff0c;好对每个版本做管理&#xff0c;可以更方便追踪历史版本代码。 一、效果图 1、git文件夹方式管理分支 二、git命令 1、在本…

SSMP整合案例(14) 将界面查询改为分页查询

前面几篇文章过后 我们的项目基本环境就算搭好了 但是 我们下面的分页显然就是个摆设 这里 我们就先将查询的方法改成分页的 我们 java项目之前做了这个分页的函数 那么 我们vue项目 直接在 src下的 api 下的bookApi.js中加上对应的函数 export function getPage(params){r…