输入框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的穿透
相关推荐
学习前端的小z5 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
星星会笑滴9 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
彭世瑜29 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40430 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish30 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five32 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序32 分钟前
vue3 封装request请求
java·前端·typescript·vue
临枫54132 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
RAY_CHEN.33 分钟前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
酷酷的威朗普33 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5