在现代 Web 应用中,用户隐私保护已成为不可忽视的设计原则。当涉及到敏感信息(如手机号、身份证号)的展示时,如何在保证功能完整性的前提下,兼顾安全性与美观性,是一个值得深入探讨的话题。

本文将带你剖析一个基于原生 HTML/CSS/JavaScript 实现的“手机号模糊输入框”,它不仅能在用户输入时正常显示,失焦后自动对中间四位进行模糊处理(如 138****5678),还支持只读模式下的默认模糊显示。整个过程不依赖任何框架,轻量高效,极具实用价值。

完整代码已附于文末,可直接复制运行!


🧩 一、功能目标

我们希望实现以下效果:

  • 用户在输入手机号时,可正常查看完整号码(便于输入校验)。
  • 输入框失焦(blur)后,若号码符合手机号格式(11位,以13-19开头),则自动将中间四位替换为模糊字符(**** 或视觉模糊效果)。
  • 支持只读输入框默认显示模糊号码。
  • 模糊效果不仅仅是文本替换,而是带有视觉模糊(blur)滤镜的高级呈现,提升 UI 美感。
  • 保持原输入框样式一致,无明显跳动或错位。

🏗️ 二、核心实现思路

该方案采用了“双层叠加 + 动态同步”的设计模式:

  1. 保留原始输入框:用于用户正常输入,处理交互逻辑。
  2. 创建一个“模糊覆盖层”:一个绝对定位的 div,覆盖在原始输入框之上,用于展示模糊后的手机号。
  3. 通过 opacity 控制显隐
    • 聚焦时:覆盖层透明,用户看到的是可编辑的原始输入框。
    • 失焦后:若输入有效,覆盖层显现,遮挡原始内容,实现“视觉模糊”。
  4. 动态内容同步:监听输入事件,实时将处理后的模糊内容渲染到覆盖层。

这种设计既保证了交互的完整性,又实现了视觉上的“模糊”效果,避免了直接修改 inputvalue 带来的输入体验问题。


🎨 三、关键技术点详解

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 未输入时也正常显示提示
✅ 可扩展性强 可轻松适配身份证、银行卡等其他敏感信息

🚀 五、可优化方向

虽然当前实现已非常完善,但仍有一些可提升空间:

  1. 性能优化:高频 scroll 事件可加防抖。
  2. 移动端适配:考虑软键盘弹出对布局的影响。
  3. A11y(无障碍):为模糊层添加 aria-hidden="true",避免屏幕阅读器误读。
  4. 自定义配置:封装为可配置组件,支持模糊位数、滤镜强度等参数。
  5. 暗色模式适配:根据主题动态调整背景色与阴影。

📌 六、适用场景

  • 用户资料页的手机号展示
  • 订单详情中的联系方式
  • 后台管理系统中的敏感信息脱敏
  • 任何需要“输入时可见,展示时模糊”的交互场景

🧪 七、结语

这个“手机号模糊输入框”方案,巧妙地利用了分层渲染 + 动态同步的思路,在不牺牲用户体验的前提下,实现了优雅的隐私保护 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>
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐