1. 为什么要自定义呢?
原生的 input 和 textarea 在某些特定场景下存在功能或兼容性限制,因此使用 div 元素自定义实现,突破原生输入框在样式、功能、兼容性上的限制。
1、解决火狐浏览器换行问题
某些版本的火狐浏览器中,原生 textarea 存在回车不换行而显示为空格的问题。这种行为在开发文本编辑器或多行输入框时造成不便。
2、提供更灵活的样式和内容格式
原生 input 和 textarea 元素样式定制有限,比如:无法直接改变 textarea 的行高或在内容中插入动态元素,如:标签或图标。原生输入框的内容只能是纯文本,难以直接支持复杂的文本格式,如:加粗、斜体或自定义样式。
3、跨平台或多浏览器兼容
不同的浏览器和设备对原生输入框的支持程度有所不同,尤其是一些老旧浏览器或特定版本的浏览器可能会对 input 和 textarea 产生不同的渲染结果。
2. 实现方法
contenteditable = "true" 是一个 HTML 属性,能够将 div 或其他元素变成一个可编辑的区域。用户可以输入文本,而浏览器会处理光标、输入等基本交互行为。通过监听相应的事件,可以让这个可编辑的 div 元素实现更复杂的行为和功能。
2.1 实现带 placeholder 的输入框
div 本身并没有内建的 placeholder 属性,但可以使用 CSS 和 JavaScript 来模拟该功能。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><style>.custom-input {border: 1px solid #ccc;padding: 5px;border-radius: 4px;outline: none;word-wrap: break-word; /* 自动换行 */white-space: pre-wrap; /* 支持空格换行 */overflow-y: auto;position: relative;}.custom-input-wrapper {position: relative;}.custom-input:empty::before {content: attr(data-placeholder); /* 使用 data-placeholder */color: #aaa;position: absolute;top: 5px;left: 8px;pointer-events: none;}</style></head><body><div class="custom-input-wrapper"><div class="custom-input" contenteditable="true" data-placeholder="请输入内容..."></div></div><script>const inputBox = document.querySelector('.custom-input');// 监听输入事件,动态处理内容为空时显示 placeholderinputBox.addEventListener('input', () => {if (!inputBox.textContent.trim()) {inputBox.setAttribute('data-placeholder', '请输入内容...');} else {inputBox.removeAttribute('data-placeholder');}});</script></body>
</html>
效果展示:
2.2 实现内容插入到光标位置
插入内容到光标位置是自定义输入框中非常常见的需求,例如:在文本框中插入表情符号或链接等。通过 JavaScript 中的 Range 和 Selection API,可以精确控制光标位置。
<button>插入内容</button>
<div class="custom-input" contenteditable="true" data-placeholder="请输入内容..."></div><script>function insertTextAtCursor(text) {const selection = window.getSelection();const range = selection.getRangeAt(0);range.deleteContents(); // 删除选中内容const textNode = document.createTextNode(text);range.insertNode(textNode); // 插入新内容range.setStartAfter(textNode); // 设置光标到插入文本后range.collapse(true); // 合并范围selection.removeAllRanges();selection.addRange(range);}document.querySelector('button').addEventListener('click', () => {insertTextAtCursor('插入的内容');});
</script>
效果展示:
3. 优缺点
3.1 优点
1、兼容性
使用 word-wrap: break-word 和 white-space: pre-wrap,可以确保不同浏览器中实现的换行行为一致。
2、灵活性
可以完全自定义样式和行为,比如控制光标的样式、输入框的大小、边框样式等。此外,使用 JavaScript 可以更加方便地动态插入和编辑内容。
3、支持插入光标位置
通过 Selection 和 Range API,可以精确的将文本插入到当前光标位置。
3.2 缺点
1、事件监听复杂
与原生的 input 和 textarea 不同,设置 contenteditable = "true" 的元素需要手动管理 placeholder 的显示与隐藏,处理焦点事件(focus、blur)和输入事件等,增加开发复杂性。
2、内容格式问题
由于 div 中的内容是可以包含 HTML 的,用户可能会误输入 HTML 标签或其他不合适的内容,要求开发者处理内容时格外小心,避免引发 XSS(跨站脚本攻击)等安全问题,且需要对输入内容进行过滤和清理。
3、性能问题
对于输入内容较长的情况,频繁的 DOM 操作可能会导致性能下降,尤其是当需要处理大量内容时,可能会影响用户体验。
4. 存在问题
4.1 控制输入内容长度
contenteditable 默认不限制输入,需要额外监听 input 事件并判断长度。
inputBox.addEventListener('input', () => {const maxLength = 200;if (inputBox.textContent.length > maxLength) {inputBox.textContent = inputBox.textContent.slice(0, maxLength);}
});
4.2 处理用户粘贴带有 HTML 内容
用户粘贴内容时,可能会不小心粘贴带有 HTML 标签的内容。我们可以通过监听 paste
事件,过滤粘贴内容,只保留纯文本:
inputBox.addEventListener('paste', (event) => {event.preventDefault();const text = event.clipboardData.getData('text/plain');document.execCommand('insertText', false, text);
});
总结
使用 contenteditable="true" 属性,可以实现一个高度自定义、功能强大的输入框,克服原生 input 和 textarea 的局限性。