如何为contenteditable=true的div实现逐字符消失的占位符效果?
嘿,这个需求我刚好研究过!用contenteditable div实现这种逐字符消失的占位符完全没问题,下面给你一套完整的解决方案,亲测好用:
核心实现思路
要达成这个效果,核心是把用户输入和剩余占位符无缝结合展示:
- 用
contenteditablediv作为输入容器,它比原生input更灵活,能自定义内容的渲染逻辑 - 借助CSS伪元素显示剩余占位符,通过JS实时计算已输入字符,动态更新占位符的剩余部分
- 处理好光标位置,确保用户输入时光标始终停在自己输入的内容后面,不会跑到占位符区域里
完整代码实现
HTML
<div class="birthday-input" contenteditable="true" data-original-placeholder="MM/DD/YYYY" data-placeholder="MM/DD/YYYY"></div>
这里用两个data-*属性:data-original-placeholder存储原始占位符文本,方便输入清空时恢复;data-placeholder存储当前剩余的占位符内容,供CSS伪元素读取。
CSS
.birthday-input { position: relative; width: 200px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; font-family: system-ui, sans-serif; min-height: 20px; } /* 默认显示完整占位符,浅灰色且不干扰输入 */ .birthday-input::after { content: attr(data-placeholder); position: absolute; top: 10px; left: 10px; color: #aaa; pointer-events: none; white-space: nowrap; } /* 有输入时,让占位符紧跟在输入内容后面 */ .birthday-input.has-content::after { left: calc(10px + var(--input-content-width)); }
JavaScript
const inputDiv = document.querySelector('.birthday-input'); const originalPlaceholder = inputDiv.dataset.originalPlaceholder; // 辅助函数:计算文本的实际渲染宽度 function getTextWidth(text) { const tempEl = document.createElement('span'); tempEl.style.visibility = 'hidden'; tempEl.style.position = 'absolute'; tempEl.style.font = window.getComputedStyle(inputDiv).font; document.body.appendChild(tempEl); tempEl.textContent = text; const width = tempEl.offsetWidth; document.body.removeChild(tempEl); return width; } // 输入事件处理:更新剩余占位符和样式 inputDiv.addEventListener('input', () => { const userInput = inputDiv.textContent.trim(); // 只保留数字,符合生日输入的场景 const cleanedInput = userInput.replace(/[^0-9]/g, ''); if (cleanedInput.length === 0) { // 无输入时恢复原始占位符 inputDiv.dataset.placeholder = originalPlaceholder; inputDiv.classList.remove('has-content'); inputDiv.style.removeProperty('--input-content-width'); return; } // 根据输入长度生成剩余占位符:替换掉已输入对应的占位符字符 let placeholderIndex = 0; const remainingPlaceholder = originalPlaceholder.split('').map(char => { // 只替换占位符中的字母(M/D/Y),分隔符/保留 if (/[A-Z]/.test(char) && placeholderIndex < cleanedInput.length) { placeholderIndex++; return ''; } return char; }).join(''); // 更新占位符和样式 inputDiv.dataset.placeholder = remainingPlaceholder; inputDiv.classList.add('has-content'); inputDiv.style.setProperty('--input-content-width', `${getTextWidth(userInput)}px`); // 强制光标停在输入内容末尾,避免错位 const range = document.createRange(); const selection = window.getSelection(); range.selectNodeContents(inputDiv); range.collapse(false); selection.removeAllRanges(); selection.addRange(range); }); // 失去焦点时,若为空则恢复原始占位符 inputDiv.addEventListener('blur', () => { if (!inputDiv.textContent.trim()) { inputDiv.dataset.placeholder = originalPlaceholder; inputDiv.classList.remove('has-content'); } }); // 获得焦点时,无输入则光标定位在开头 inputDiv.addEventListener('focus', () => { if (!inputDiv.textContent.trim()) { const range = document.createRange(); const selection = window.getSelection(); range.selectNodeContents(inputDiv); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); } });
关键细节说明
- 占位符更新逻辑:遍历原始占位符的每个字符,遇到M/D/Y这类需要用户输入的位置,就用用户输入的数字“替换”(实际是清空占位符的对应字符),分隔符/则保留在剩余占位符中
- 光标处理:每次输入后强制将光标移到输入内容末尾,避免用户不小心点击到占位符区域导致光标错位
- 动态宽度计算:用临时span计算用户输入内容的实际渲染宽度,通过CSS变量调整占位符的
left值,让它完美紧跟在输入内容后面 - 输入清理:只保留用户输入的数字,避免非数字字符干扰占位符的匹配逻辑
你可以直接复制代码测试,还能根据需求调整样式和占位符格式(比如改成DD/MM/YYYY也完全适配)。
内容的提问来源于stack exchange,提问作者moonchunkcheese
相关产品推荐
相关产品推荐

