You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为contenteditable=true的div实现逐字符消失的占位符效果?

嘿,这个需求我刚好研究过!用contenteditable div实现这种逐字符消失的占位符完全没问题,下面给你一套完整的解决方案,亲测好用:

核心实现思路

要达成这个效果,核心是把用户输入和剩余占位符无缝结合展示:

  • 用contenteditable div作为输入容器,它比原生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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:18:35