实时回文文本编辑器的核心需求是用户在输入框输入内容时,编辑器区域能实时展示对应的回文结果,同时需要过滤掉指定类型的字符,还要保证用户操作光标时,回文区域的展示和光标位置不会出现错乱。这个功能涉及输入事件监听、字符处理、光标位置计算等多个前端技术点。

核心功能拆解
要实现完整的实时回文文本编辑器,需要拆分三个核心模块:
- 字符过滤模块:按照预设规则过滤输入内容中的非法字符,比如过滤数字、特殊符号,或者只保留中英文等。
- 回文生成模块:将过滤后的合法字符实时生成回文内容,比如输入abc,生成abccba或者abcba。
- 光标同步模块:保证输入框和回文展示区域的光标位置对应,避免用户操作光标时出现内容错位。
字符过滤策略实现
字符过滤可以在输入事件触发时执行,优先使用input事件监听,因为input事件可以覆盖粘贴、拖拽输入等多种输入场景。过滤逻辑可以根据需求自定义,比如下面的示例实现过滤所有非中英文的字符:
// 字符过滤函数,只保留中文字符、英文字母
function filterChar(inputStr) {
// 正则匹配中英文,其他字符替换为空
return inputStr.replace(/[^a-zA-Zu4e00-u9fa5]/g, '');
}
如果需要支持更多过滤规则,比如过滤空格、过滤指定字符,可以在正则中调整匹配规则,或者拆分多个过滤条件依次处理。
回文内容生成逻辑
回文生成分为两种常见类型,一种是奇回文,比如输入abc生成abcba;一种是偶回文,比如输入abc生成abccba。下面的示例实现偶回文生成,将过滤后的字符串和反转后的字符串拼接:
// 生成偶回文内容
function generatePalindrome(filteredStr) {
if (!filteredStr) return '';
// 将字符串转为数组后反转,再拼接原字符串
const reversedStr = filteredStr.split('').reverse().join('');
return filteredStr + reversedStr;
}
如果需要生成奇回文,只需要去掉反转后字符串的第一个字符再拼接即可,比如return filteredStr + reversedStr.slice(1);。
光标同步实现方案
光标同步是实时回文编辑器最容易出问题的部分,核心逻辑是:当输入框的光标位置发生变化时,需要计算回文展示区域对应的光标位置。因为回文内容是原字符串拼接反转字符串,所以光标位置的计算规则如下:
假设输入框的光标位置在索引pos(从0开始),过滤后的字符串长度为len,那么回文区域的光标位置为:
- 如果
pos <= len,回文光标位置为pos - 如果
pos > len,回文光标位置为len + (len - (pos - len)),也就是2*len - pos
下面是完整的光标同步实现代码:
// 光标同步函数,inputEl是输入框元素,palindromeEl是回文展示元素
function syncCursor(inputEl, palindromeEl, filteredStr) {
// 获取输入框当前光标位置
const inputCursorPos = inputEl.selectionStart;
const filteredLen = filteredStr.length;
let palindromeCursorPos = 0;
if (inputCursorPos <= filteredLen) {
palindromeCursorPos = inputCursorPos;
} else {
palindromeCursorPos = 2 * filteredLen - inputCursorPos;
}
// 设置回文展示元素的光标位置,需要元素支持可编辑,比如设置contenteditable为true
if (palindromeEl.setSelectionRange) {
palindromeEl.setSelectionRange(palindromeCursorPos, palindromeCursorPos);
} else if (palindromeEl.createTextRange) {
// 兼容IE浏览器
const range = palindromeEl.createTextRange();
range.move('character', palindromeCursorPos);
range.select();
}
}
完整示例代码
下面是整合所有功能的完整示例,包含一个输入框和回文展示区域,实现实时回文生成、字符过滤和光标同步:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>实时回文文本编辑器</title>
<style>
.editor-container {
width: 600px;
margin: 20px auto;
}
.input-area, .palindrome-area {
width: 100%;
height: 100px;
padding: 10px;
box-sizing: border-box;
border: 1px solid #ccc;
margin-bottom: 10px;
font-size: 16px;
}
.palindrome-area {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div class="editor-container">
<h3>输入区域(只支持中英文输入)</h3>
<textarea class="input-area" id="inputArea"></textarea>
<h3>实时回文展示区域</h3>
<div class="palindrome-area" id="palindromeArea" contenteditable="true"></div>
</div>
<script>
const inputArea = document.getElementById('inputArea');
const palindromeArea = document.getElementById('palindromeArea');
// 字符过滤函数
function filterChar(inputStr) {
return inputStr.replace(/[^a-zA-Zu4e00-u9fa5]/g, '');
}
// 生成偶回文函数
function generatePalindrome(filteredStr) {
if (!filteredStr) return '';
const reversedStr = filteredStr.split('').reverse().join('');
return filteredStr + reversedStr;
}
// 光标同步函数
function syncCursor(inputEl, palindromeEl, filteredStr) {
const inputCursorPos = inputEl.selectionStart;
const filteredLen = filteredStr.length;
let palindromeCursorPos = 0;
if (inputCursorPos <= filteredLen) {
palindromeCursorPos = inputCursorPos;
} else {
palindromeCursorPos = 2 * filteredLen - inputCursorPos;
}
// 设置回文区域的光标位置
if (palindromeEl.setSelectionRange) {
palindromeEl.setSelectionRange(palindromeCursorPos, palindromeCursorPos);
} else if (palindromeEl.createTextRange) {
const range = palindromeEl.createTextRange();
range.move('character', palindromeCursorPos);
range.select();
}
}
// 监听输入事件
inputArea.addEventListener('input', function() {
const rawValue = this.value;
// 过滤字符
const filteredValue = filterChar(rawValue);
// 生成回文
const palindromeValue = generatePalindrome(filteredValue);
// 更新回文区域内容
palindromeArea.textContent = palindromeValue;
// 同步光标
syncCursor(this, palindromeArea, filteredValue);
});
// 监听输入框光标变化事件,保证光标移动时同步
inputArea.addEventListener('click', function() {
const rawValue = this.value;
const filteredValue = filterChar(rawValue);
syncCursor(this, palindromeArea, filteredValue);
});
inputArea.addEventListener('keyup', function(e) {
// 排除输入字符的keyup事件,避免重复处理
if (e.key !== 'Meta' && e.key !== 'Control' && e.key !== 'Alt' && e.key !== 'Shift') {
return;
}
const rawValue = this.value;
const filteredValue = filterChar(rawValue);
syncCursor(this, palindromeArea, filteredValue);
});
</script>
</body>
</html>
注意事项
实际开发中还需要注意几个问题:一是输入事件的处理频率,如果回文生成逻辑比较复杂,可以使用防抖处理,避免频繁计算导致页面卡顿;二是回文展示区域如果设置为可编辑,需要额外监听回文区域的输入事件,反向同步到输入框,避免内容不一致;三是特殊字符的过滤规则需要根据实际需求调整,比如如果需要保留空格,只需要修改过滤函数的正则即可。