Element UI el-input readonly 灰底失效:inline style 只到外层 div,::v-deep 才能穿进去
某后台管理系统里有一个弹窗,其中有个字段是"系统自动填入、用户不可编辑"的场景。开发给 <el-input> 加了 readonly 属性,又顺手在 style 里写了灰底色,期待用户看一眼就知道这个框不能改——结果验收时用户反馈"和正常输入框没任何区别",用手点了好几次才发现不能编辑。
现象
<!-- 开发以为这样就有灰底了 -->
<el-input v-model="fieldValue" readonly style="background-color: #f5f7fa;" />浏览器渲染出来是白底,cursor 也没变,与可编辑 input 视觉上毫无差异。即便换成 scoped style 直接写:
/* scoped 里这样写也不生效 */
.el-input__inner {
background-color: #f5f7fa;
}依然无效。
根因
el-input 本质是一个 wrapper 组件,其模板大致是:
<div class="el-input">
<input class="el-input__inner" />
</div>inline style 经 Vue 的 attribute fallthrough 机制,只落到根元素——也就是外层 <div class="el-input">,不会下传到内部的 <input> 元素。Element UI 在 el-input__inner 上自带 background: #fff 等完整样式,优先级足以覆盖外层 div 上那个 background。
第二个坑是 Vue 2 scoped style 的 data-v 隔离。Vue 2 编译时给 scoped 选择器追加 [data-v-xxxxxx] attribute selector,但 element-ui 内部生成的 input 元素上没有当前组件的 hash 属性,导致选择器完全命中不到。
- inline style / 非 scoped 给外层 div → 被 .el-input__inner 自带样式覆盖
- scoped style 直接写 .el-input__inner → data-v hash 不匹配,选择器失效
两条路都走不通,背景色永远白底。
正解
用自定义 class 做范围限定,再用 ::v-deep 穿透 scoped 隔离,直接命中内层 input:
<template>
<el-input v-model="fieldValue" readonly class="my-readonly" />
</template>
<style scoped>
::v-deep .my-readonly .el-input__inner {
background-color: #f5f7fa;
color: #606266;
cursor: not-allowed;
}
</style>- 给
<el-input>加自定义 class(my-readonly)做范围限定,防止污染同页其它 input ::v-deep是 Vue 2 scoped 提供的穿透标记,它后面的选择器跳过 data-v 限制,以普通 CSS 匹配子组件内部 DOM- 真正要改的是
.el-input__inner,不是外层.el-input - 顺手加
cursor: not-allowed比单纯灰底更直观——鼠标悬停即反馈
- Vue 3:改用
:deep(.my-readonly .el-input__inner) - 更老的项目:可能用
/deep/或>>>穿透语法,效果相同 - 不要用全局非 scoped style(去掉
scoped属性)来绕过问题——会影响整站所有 input
踩坑备忘:readonly vs disabled
容易误用 :disabled="true" 替代 readonly——element-ui 2.x 的 disabled 确实自带灰化样式,视觉上"好像对了",但语义错了:disabled 字段在 HTML 表单提交时不参与提交,而 readonly 才是"不可编辑但参与提交"的正确语义。如果这个字段的值需要被后端接收,绝对不能用 disabled。
一句话外卖
el-input 是 wrapper 组件,inline style 只到外层 div;要改内层 input 的外观,必须自定义 class +
::v-deep .your-class .el-input__inner,这是 Vue 2 scoped + Element UI 组件封装双重隔离下的唯一可靠解法。