React中如何基于自定义词表为textarea实现拼写检查并添加红色波浪线
React中基于自定义词表的拼写检查实现(带红色波浪线)
原生<textarea>依赖系统词典,无法满足自定义词表的拼写检查需求,且不能单独为错误单词添加样式。下面是基于contenteditable模拟textarea的实现方案,支持自定义词表检查和红色波浪线标记。
核心实现思路
用contenteditable的<div>替代原生textarea,原因是它允许给单个文本片段添加样式;配合自定义拼写检查逻辑,将不在词表中的单词用带波浪线样式的<span>包裹;同时处理光标位置,避免重新渲染后光标丢失。
完整代码实现
组件代码
import { useState, useRef, useEffect } from 'react'; const CustomSpellCheckEditor = () => { // 自定义词表:可根据需求动态更新(比如从后端加载) const customDictionary = new Set(['react', '拼写检查', '波浪线', '组件', 'javascript', 'frontend']); const [content, setContent] = useState(''); const editorRef = useRef(null); // 保存当前光标位置 const saveCursorPos = () => { const selection = window.getSelection(); if (!selection.rangeCount) return 0; const range = selection.getRangeAt(0); const preRange = range.cloneRange(); preRange.selectNodeContents(editorRef.current); preRange.setEnd(range.startContainer, range.startOffset); return preRange.toString().length; }; // 恢复光标位置 const restoreCursorPos = (pos) => { const selection = window.getSelection(); const range = document.createRange(); range.setStart(editorRef.current, 0); range.collapse(true); const textNode = editorRef.current.firstChild; if (textNode) { range.setEnd(textNode, Math.min(pos, textNode.length)); } selection.removeAllRanges(); selection.addRange(range); }; // 拼写检查并格式化内容 const formatContent = (text) => { if (!text) return ''; // 拆分文本为「中文词/英文词/标点符号」,可根据需求优化正则 const splitRegex = /([\u4e00-\u9fa5]+|[a-zA-Z]+|[^\w\s])/g; const parts = text.match(splitRegex) || []; return parts.map(part => { // 判断是否为需要检查的单词(过滤标点) const isCheckable = /[\u4e00-\u9fa5]|[a-zA-Z]/.test(part); if (isCheckable && !customDictionary.has(part.toLowerCase())) { return `<span class="spell-error">${part}</span>`; } return part; }).join(''); }; // 处理输入事件 const handleInput = (e) => { const currentText = e.target.innerText; const cursorPos = saveCursorPos(); setContent(currentText); // 异步恢复光标,确保DOM更新完成 setTimeout(() => restoreCursorPos(cursorPos), 0); }; // 内容更新时重新渲染格式化后的HTML useEffect(() => { if (editorRef.current) { editorRef.current.innerHTML = formatContent(content); } }, [content]); return ( <div ref={editorRef} contentEditable onInput={handleInput} className="custom-editor" spellCheck="false" // 禁用原生拼写检查 placeholder="请输入内容..." /> ); }; export default CustomSpellCheckEditor;
样式代码
.custom-editor { width: 100%; min-height: 160px; padding: 10px; border: 1px solid #d1d5db; border-radius: 6px; font-family: system-ui, -apple-system, sans-serif; font-size: 16px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: break-word; outline: none; resize: vertical; background-color: white; } .spell-error { text-decoration: underline wavy #ef4444; text-decoration-thickness: 1px; text-underline-offset: 2px; }
关键细节说明
- 词表效率:用
Set存储词表,查询时间复杂度为O(1),比数组更高效。 - 文本拆分:示例正则适配中英文混合场景,可根据需求扩展(比如处理连字符、缩写等)。
- 光标处理:通过
Selection和RangeAPI保存/恢复光标,解决重新渲染后光标丢失的问题。 - 样式模拟:通过
white-space: pre-wrap、resize等属性让编辑器外观和行为接近原生textarea。
可选优化方向
- 动态更新词表:添加输入框让用户自定义添加新词,直接更新
customDictionary即可生效。 - 局部检查优化:长文本场景下,仅检查光标附近的单词,减少计算量。
- 错误单词右键菜单:添加右键选项(比如添加到词表、忽略),提升交互体验。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

