输入框el-input居中, 并且自适应文字内容宽度

效果预览

适用场景

可编辑的顶部标题

使用组件

  • el-container
  • el-header
  • el-input

起始组件内容

vue 复制代码
<template>
  <div class="common-layout">
    <el-container class="container">
      <el-header class="header">
        <el-input v-model="title" />
      </el-header>
    </el-container>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const title = ref('这是标题')
</script>
<style scoped></style>

起始的效果

虽然输入框只有四个字"这是标题", 但输入框的宽度几乎占满了浏览器的宽度

现在输入框的宽度几乎占满浏览器的宽度, 我们要做的是输入框的宽度随着字数的增加而增加, 所以, 我们先给输入框设置一个初始的合适的宽度,

vue 复制代码
<el-input style="width: 6em" v-model="title" />

再让el-input居中

ini 复制代码
<el-header class="header">
css 复制代码
.header {
  display: flex;
  place-content: center;
}

输入框长度随着文字增加而增加

这个肯定要监听输入框的事件, 那么怎么获取输入框的引用呢?

获取输入框的引用

模板引用 肯定用不了, 因为el-input里面还是个组件, 所以, 我选择用原始的js代码

ini 复制代码
// <el-input class="title" style="width: 6em" v-model="title" />
var inner_input = document.querySelector('.title .el-input__inner')

监听输入框事件

javascript 复制代码
inner_input.addEventListener('input', function () {
	...
})

现在可以监听输入框事件了, 我们要输入文字, 然后改变输入框的宽度

改变输入框宽度

ini 复制代码
// <el-input class="title" :style="`width:${titleInputWidth}em`" v-model="title" />
onMounted(() => {
  var inner_input = document.querySelector('.title .el-input__inner')
  inner_input.addEventListener('input', function () {
    titleInputWidth.value = this.value.length
  })
})

这个时候的输入框就, 就可以随着输入文字, 输入框的宽度就随之改变.

输入框最小宽度

当我们把输入框的文字清空之后, 发现输入框变的很狭窄

设置最小宽度

css 复制代码
// <el-input class="title" :style="`width:${titleInputWidth}em`" v-model="title" />
.title {
  min-width: 6em;
}

增加一个汉字, 宽度应该增加多少?

下面这个是"这是标题这是标题"

可以看到, 最后的两个字"标题"看不见了, 所以, 增加一个汉字, 只增加1em就不行, 那么应该加多少呢?

一个字符宽度多少?

我们把中英文数字都测一下, 可以得到准确的宽度, 当然了, 这种宽度仅限于当前被测的字符.

xml 复制代码
<template>
  <div class="common-layout">
    <el-container class="container">
      <el-header class="header">
        <div class="outer-container">
          <el-input class="title" :style="`width:${titleInputWidth}px`" v-model="title" />
          <div class="inner-container">
            <span ref="measureRefHan" :style="{ visibility: 'hidden', position: 'absolute' }">{{
              testCharHan
            }}</span>
            <span ref="measureRefLetter" :style="{ visibility: 'hidden', position: 'absolute' }">{{
              testCharLetter
            }}</span>
            <span ref="measureRefNumber" :style="{ visibility: 'hidden', position: 'absolute' }">{{
              testCharNumber
            }}</span>
          </div>
        </div>
      </el-header>
    </el-container>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const title = ref('这是标题')
const titleInputWidth = ref(6)
const measureRefHan = ref(null)
const measureRefLetter = ref(null)
const measureRefNumber = ref(null)
const testCharHan = '汉'
const testCharLetter = 'a'
const testCharNumber = '0'
// 定义函数来判断字符类型
function isChineseCharacter(char) {
  return /[\u4e00-\u9fff]/.test(char)
}
function isEnglishOrDigit(char) {
  return /[a-zA-Z0-9]/.test(char)
}
onMounted(() => {
  const hanCharWidth = measureRefHan.value.offsetWidth
  const letterCharWidth = measureRefLetter.value.offsetWidth
  const numberCharWidth = measureRefNumber.value.offsetWidth
  console.log(`The width of the character "${testCharHan}" is ${hanCharWidth}px.`)
  console.log(`The width of the character "${testCharLetter}" is ${letterCharWidth}px.`)
  console.log(`The width of the character "${testCharNumber}" is ${numberCharWidth}px.`)
  var inner_input = document.querySelector('.title .el-input__inner')
  inner_input.addEventListener('input', function () {
    let currentWidth = 0
    for (let i = 0; i < this.value.length; i++) {
      const char = this.value[i]
      if (isChineseCharacter(char)) {
        currentWidth += hanCharWidth
      } else if (isEnglishOrDigit(char)) {
        currentWidth += letterCharWidth
      }
    }
    titleInputWidth.value = currentWidth
  })
})
</script>
<style scoped>
.header {
  display: flex;
  place-content: center;
}
.title {
  min-width: 6em;
}
.outer-container {
  background-color: aqua;
  width: 100%;
  display: flex;
  place-content: center;
}
.inner-container {
  display: flex;
}
</style>

可以看到, 我打了个很多个1111111, 但是数字1和0的宽度明显不一样, 而且最不顺眼的是输入框的文字不居中,

那么, 我们先把居中搞上

输入框文字居中

deep深层选择器

css 复制代码
:deep(.title .el-input__inner) {
  text-align: center;
}

输入框文字居中了, 但是字符宽度还没解决, 不过作为一个可修改的标题组件, 基本够用了. 对于能用就行的人来说, 就可以不用往下看了.

自适应宽度的组件有哪些?

尤其是针对文字的, 常用的不是p标签, 就是span标签, 我们就用span标签来测量字符串的正确宽度

下面这个span调试的时候是可见的, 调试完以后就改为不可见 visibility: 'hidden'

xml 复制代码
<template>
  <div class="common-layout">
    <el-container class="container">
      <el-header class="header">
        <div class="outer-container">
          <el-input class="title" :style="`width:${titleInputWidth}px`" v-model="title" />
          <div class="inner-container">
            <span ref="measureSpanRef" :style="{ visibility: 'visible', position: 'absolute' }">{{
              title
            }}</span>
          </div>
        </div>
      </el-header>
    </el-container>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const title = ref('这是标题')
const titleInputWidth = ref(0)
const measureSpanRef = ref(null)
onMounted(() => {
  const inner_input = document.querySelector('.title .el-input__inner')
  inner_input.addEventListener('input', function () {
    var offsetWidth = measureSpanRef.value.offsetWidth
    titleInputWidth.value = offsetWidth
  })
})
</script>
<style scoped>
.header {
  display: flex;
  place-content: center;
}
.title {
  min-width: 6em;
  place-content: center;
}
.outer-container {
  position: relative;
  width: 100%;
  background-color: aqua;
  width: 100%;
  display: flex;
  place-content: center;
}
.inner-container {
  display: flex;
  place-content: center;
  position: absolute; /* 绝对定位 */
  width: 100%; /* 容器宽度占满整个网页 */
}
:deep(.title .el-input__inner) {
  text-align: center;
  width: auto;
}
</style>

可以看到, span和input的字体大小不一样, 所以我们要把两个组件的字体大小设置一样,

css 复制代码
.measure-span {
  font-size: 16px;
  font-family: Arial, sans-serif;
  font-weight: normal;
  font-style: normal;
  letter-spacing: normal;
  white-space: nowrap;
}

这下在只有一行文字的情况下, 可以保证span和input的文字宽度是一样的啦, 也不用自己去测量每个字符的宽度啦, 直接使用span的宽度.

贴一下最终的代码

vue 复制代码
<template>
  <div class="common-layout">
    <el-container class="container">
      <el-header class="header">
        <div class="outer-container">
          <el-input class="title" :style="`width:${titleInputWidth}px`" v-model="title" />
          <div class="inner-container">
            <span
              ref="measureSpanRef"
              class="measure-span"
              :style="{ visibility: 'hidden', position: 'absolute' }"
              >{{ title }}</span
            >
          </div>
        </div>
      </el-header>
    </el-container>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const title = ref('这是标题')
const titleInputWidth = ref(0)
const measureSpanRef = ref(null)
onMounted(() => {
  const inner_input = document.querySelector('.title .el-input__inner')
  inner_input.addEventListener('input', function () {
    var offsetWidth = measureSpanRef.value.offsetWidth
    titleInputWidth.value = offsetWidth + 32
  })
})
</script>
<style scoped>
.header {
  display: flex;
  place-content: center;
}
.title {
  min-width: 6em;
  place-content: center;
}
.outer-container {
  position: relative;
  width: 100%;
  background-color: aqua;
  width: 100%;
  display: flex;
  place-content: center;
}
.inner-container {
  display: flex;
  place-content: center;
  position: absolute; /* 绝对定位 */
  width: 100%;
}
:deep(.title .el-input__inner) {
  text-align: center;
  width: auto;
  font-size: 16px;
  font-family: Arial, sans-serif;
  font-weight: normal;
  font-style: normal;
  letter-spacing: normal;
  white-space: nowrap;
}
.measure-span {
  font-size: 16px;
  font-family: Arial, sans-serif;
  font-weight: normal;
  font-style: normal;
  letter-spacing: normal;
  white-space: nowrap;
}
</style>

titleInputWidth.value = offsetWidth + 32 加32是因为input的父组件有padding, 32抵消了padding, 使得字符串左右保留了一小段空白, 让人能看到字符串的头尾.

知识点

  • 上下层组件用绝对定位, 就像span一样, span在input上层.
  • deep深层选择器, vue3的穿透
相关推荐
别拿曾经看以后~34 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死37 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍