【一叶障目】[特殊字符] 实现一个优雅的手机号模糊输入框:前端 UI 与交互设计实践【AI撰稿】
特性说明✅无框架依赖纯原生 JS/CSS,轻量、兼容性好✅视觉一致性精确复制原输入框样式,无错位✅高级模糊效果使用,非简单****✅支持滚动同步长内容也能对齐✅兼容 placeholder未输入时也正常显示提示✅可扩展性强可轻松适配身份证、银行卡等其他敏感信息这个“手机号模糊输入框”方案,巧妙地利用了分层渲染 + 动态同步的思路,在不牺牲用户体验的前提下,实现了优雅的隐私保护 UI。它不仅解决了功
在现代 Web 应用中,用户隐私保护已成为不可忽视的设计原则。当涉及到敏感信息(如手机号、身份证号)的展示时,如何在保证功能完整性的前提下,兼顾安全性与美观性,是一个值得深入探讨的话题。
本文将带你剖析一个基于原生 HTML/CSS/JavaScript 实现的“手机号模糊输入框”,它不仅能在用户输入时正常显示,失焦后自动对中间四位进行模糊处理(如 138****5678
),还支持只读模式下的默认模糊显示。整个过程不依赖任何框架,轻量高效,极具实用价值。
完整代码已附于文末,可直接复制运行!
🧩 一、功能目标
我们希望实现以下效果:
- 用户在输入手机号时,可正常查看完整号码(便于输入校验)。
- 输入框失焦(blur)后,若号码符合手机号格式(11位,以13-19开头),则自动将中间四位替换为模糊字符(
****
或视觉模糊效果)。 - 支持只读输入框默认显示模糊号码。
- 模糊效果不仅仅是文本替换,而是带有视觉模糊(blur)滤镜的高级呈现,提升 UI 美感。
- 保持原输入框样式一致,无明显跳动或错位。
🏗️ 二、核心实现思路
该方案采用了“双层叠加 + 动态同步”的设计模式:
- 保留原始输入框:用于用户正常输入,处理交互逻辑。
- 创建一个“模糊覆盖层”:一个绝对定位的
div
,覆盖在原始输入框之上,用于展示模糊后的手机号。 - 通过
opacity
控制显隐:- 聚焦时:覆盖层透明,用户看到的是可编辑的原始输入框。
- 失焦后:若输入有效,覆盖层显现,遮挡原始内容,实现“视觉模糊”。
- 动态内容同步:监听输入事件,实时将处理后的模糊内容渲染到覆盖层。
这种设计既保证了交互的完整性,又实现了视觉上的“模糊”效果,避免了直接修改 input
的 value
带来的输入体验问题。
🎨 三、关键技术点详解
1. 结构设计:双层容器
<div class="input-container">
<input class="original-input" id="phoneInput" placeholder="请输入手机号码" maxlength="11">
</div>
每个输入框都被包裹在一个相对定位的容器中,为后续绝对定位的“覆盖层”提供定位基准。
2. 创建模糊覆盖层(Blur Overlay)
在 JavaScript 中,为每个 .original-input
动态创建一个 div.blur-overlay
,并插入其父容器中:
const blurOverlay = document.createElement('div');
blurOverlay.className = 'blur-overlay original-input';
container.appendChild(blurOverlay);
关键点:
- 复用样式:通过同时添加
original-input
类,确保覆盖层与原输入框外观一致。 - 精确样式继承:使用
getComputedStyle()
获取原输入框的所有样式(字体、内边距、宽高等),确保视觉对齐。 pointer-events: none
:使覆盖层不拦截鼠标事件,用户仍可点击输入框进行聚焦。
3. 内容模糊处理逻辑
function processPhoneNumber(phone) {
if (phone.length === 11 && isPhoneNumber(phone)) {
return phone.substring(0, 3) +
'<span class="blur-digit">' + phone.substring(3, 7) + '</span>' +
phone.substring(7);
}
return phone;
}
-
使用正则
/^1[3-9]\d{9}$/
校验手机号格式。 -
仅对第4-7位数字应用
<span class="blur-digit">
,并为其添加 CSS 滤镜:.blur-digit { filter: blur(6px); text-shadow: 0 0 8px rgba(0,0,0,0.5); }
💡 与简单的
****
文本替换相比,filter: blur()
提供了更自然、更具设计感的模糊效果。
4. 内容与滚动同步
由于覆盖层是 div
,而原始输入框是 input
,需确保两者内容和滚动位置一致:
const syncContent = () => {
const value = input.value || input.placeholder;
contentSpan.innerHTML = processPhoneNumber(value);
contentSpan.style.transform = `translateX(${input.scrollLeft}px)`;
};
input.addEventListener('input', syncContent);
input.addEventListener('scroll', () => {
contentSpan.style.transform = `translateX(${input.scrollLeft}px)`;
});
- 使用
transform: translateX
模拟水平滚动,避免设置scrollLeft
可能引发的布局抖动。 - 支持
placeholder
的同步显示。
5. 显隐控制策略
根据不同输入框类型,采用不同策略:
- 可编辑输入框:
focus
→ 覆盖层opacity: 0
blur
且有值 →opacity: 1
- 只读输入框:
- 始终显示模糊内容(
opacity: 1
)
- 始终显示模糊内容(
if (!input.readOnly) {
input.addEventListener('focus', () => blurOverlay.style.opacity = '0');
input.addEventListener('blur', () => { if (input.value) blurOverlay.style.opacity = '1'; });
} else {
blurOverlay.style.opacity = '1';
}
🌟 四、设计亮点总结
特性 | 说明 |
---|---|
✅ 无框架依赖 | 纯原生 JS/CSS,轻量、兼容性好 |
✅ 视觉一致性 | 精确复制原输入框样式,无错位 |
✅ 高级模糊效果 | 使用 filter: blur() + text-shadow ,非简单 **** |
✅ 支持滚动同步 | 长内容也能对齐 |
✅ 兼容 placeholder | 未输入时也正常显示提示 |
✅ 可扩展性强 | 可轻松适配身份证、银行卡等其他敏感信息 |
🚀 五、可优化方向
虽然当前实现已非常完善,但仍有一些可提升空间:
- 性能优化:高频
scroll
事件可加防抖。 - 移动端适配:考虑软键盘弹出对布局的影响。
- A11y(无障碍):为模糊层添加
aria-hidden="true"
,避免屏幕阅读器误读。 - 自定义配置:封装为可配置组件,支持模糊位数、滤镜强度等参数。
- 暗色模式适配:根据主题动态调整背景色与阴影。
📌 六、适用场景
- 用户资料页的手机号展示
- 订单详情中的联系方式
- 后台管理系统中的敏感信息脱敏
- 任何需要“输入时可见,展示时模糊”的交互场景
🧪 七、结语
这个“手机号模糊输入框”方案,巧妙地利用了分层渲染 + 动态同步的思路,在不牺牲用户体验的前提下,实现了优雅的隐私保护 UI。它不仅解决了功能需求,更在细节上追求极致——从样式复制到滚动同步,处处体现对用户体验的尊重。
八、源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手机号模糊输入框</title>
<style>
/* 基础样式 */
.input-container {
position: relative;
width: 300px;
margin: 30px;
font-family: inherit; /* 继承文档字体 */
}
/* 原始输入框样式 */
.original-input {
width: 100%;
padding: 12px 15px;
font-size: 16px;
font-family: inherit;
border: 2px solid #4a90e2;
border-radius: 8px;
background: #f9f9f9;
color: #333;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
outline: none;
box-sizing: border-box; /* 确保padding不影响宽度 */
}
/* 深度模糊数字的样式 */
.blur-digit {
filter: blur(6px); /* 更强的模糊效果 */
display: inline-block;
vertical-align: top;
text-shadow: 0 0 8px rgba(0,0,0,0.5); /* 添加阴影增强模糊 */
}
/* 模糊覆盖层 */
.blur-overlay {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
white-space: nowrap;
overflow: hidden;
z-index: 2;
backdrop-filter: blur(3px); /* 轻微背景模糊增强效果 */
}
</style>
</head>
<body>
<!-- 普通输入框 -->
<div class="input-container">
<input class="original-input" id="phoneInput" placeholder="请输入手机号码" maxlength="11">
</div>
<!-- 只读输入框 -->
<div class="input-container">
<input class="original-input" readonly value="13812345678">
</div>
<script>
// 判断是否为手机号格式
function isPhoneNumber(text) {
return /^1[3-9]\d{9}$/.test(text);
}
// 处理手机号模糊显示
function processPhoneNumber(phone) {
if (phone.length === 11 && isPhoneNumber(phone)) {
return phone.substring(0, 3) +
//'****' +
'<span class="blur-digit">' + phone.substring(3, 7) + '</span>' +
phone.substring(7);
}
return phone;
}
// 为所有输入框创建模糊层
document.querySelectorAll('.original-input').forEach(input => {
const container = input.parentElement;
// 1. 创建模糊显示层
const blurOverlay = document.createElement('div');
blurOverlay.className = 'blur-overlay original-input'; // 同时使用两个类
// 2. 精确复制原输入框的所有计算样式
const computedStyle = window.getComputedStyle(input);
[
'font', 'lineHeight', 'letterSpacing', 'textIndent',
'padding', 'margin', 'border', 'width', 'height'
].forEach(prop => {
blurOverlay.style[prop] = computedStyle[prop];
});
// 3. 设置覆盖层特定样式
Object.assign(blurOverlay.style, {
display: 'flex',
alignItems: 'center', // 垂直居中
backgroundColor: 'rgba(255,255,255,0.9)',
color: computedStyle.color // 继承原输入框文字颜色
});
// 4. 创建内容容器保持精确对齐
const contentSpan = document.createElement('span');
blurOverlay.appendChild(contentSpan);
// 5. 插入到DOM
container.appendChild(blurOverlay);
// 6. 同步内容
const syncContent = () => {
const value = input.value || input.placeholder;
if (value.length === 11 && isPhoneNumber(value)) {
contentSpan.innerHTML = processPhoneNumber(value);
} else {
contentSpan.textContent = value;
}
// 精确同步滚动位置
contentSpan.style.transform = `translateX(${input.scrollLeft}px)`;
};
input.addEventListener('input', syncContent);
input.addEventListener('scroll', () => {
contentSpan.style.transform = `translateX(${input.scrollLeft}px)`;
});
// 7. 普通输入框的显隐逻辑
if (!input.readOnly) {
input.addEventListener('focus', () => {
blurOverlay.style.opacity = '0';
});
input.addEventListener('blur', () => {
if (input.value) blurOverlay.style.opacity = '1';
});
// 初始化时检查是否有值
if (input.value) blurOverlay.style.opacity = '1';
} else {
// 只读输入框始终显示
blurOverlay.style.opacity = '1';
}
// 初始化内容
syncContent();
});
</script>
</body>
</html>
更多推荐
所有评论(0)