输入框内特定单词变色处理。
场景: 将输入框看成是一个命令输入器,该输入器可以对两个特殊命令单词进行变色,如rename与stats,我们将rename与stats称为命令。命令后面可以跟其参数,如rename的参数为as,stats的参数为by;命令必须以 | 字符开头,即 | rename 才会变色,单独输入rename不做改变。一个 | 字符只能匹配一个命令,输入框可以输入多个命令,如| rename ..... | stats ....。
大致效果如下:

思路:
使用两个div输入框,将两个输入框重叠,一个用于输入,一个用于显示。为什么使用两个输入框,一是有利于数据交互,二是可以解决输入框在变色处理后光标错位问题。
输入框hide显示在表层,输入框show显示在里层,输入框hide使用css属性进行隐藏,只在其中进行输入,hide输入框的内容与show输入框进行动态绑定,让输入信息在show输入框显示。
代码如下:
<div class="show-input" contentEditable="plaintext-only" ref={show} tabIndex="-1" />
<div className="hideinput"
contentEditable="plaintext-only"
onScroll={onscroll}
onInput={onInput}
ref={hide}
/>
css:
.hide-input {
width: 100%;
padding: 12px 12px 0 12px;
height: auto;
min-height: 39px;
max-height: 70px;
position: absolute;
overflow: auto;
overflow-x: hidden;
color: transparent;
background-color: transparent;
caret-color: black; // 可能部分浏览器不生效
box-shadow: 0 0 2px @search-shadow;
}
.show-input {
width: 100%;
padding: 12px 12px 0 12px;
height: auto;
min-height: 39px;
max-height: 70px;
position: absolute;
overflow: auto;
overflow-x: hidden;
background-color: white;
box-shadow: 0 0 2px @search-shadow;
}
这里再对命令进行说明,比如以字符 | 表示一个需要变色命令的开始,如| rename的rename会变色,但是单独输入rename不变色,as单词在rename变色的情况下才会变色。相当于rename作为命令,as作为参数。

一个 | 字符只能匹配一个命令,如果现在有两个可变色的命令如rename和stats,那么 | rename stats,rename变色后,它之后的stats不在变色,除非使用 | 开启一个新的命令输入,如 | rename | stats。

大致的变色逻辑是这样。
实现过程:
先将输入字符串中的蓝色命令进行正则替换,给其添加变色标签,然后再将字符串按字符 | 分割成一个数组,对数组中每一项进行正则处理,实现相应命令的参数的变色。
//命令蓝色
const repCommand = ['rename','stats'];
//命令参数橙色
const repParamOrange = ['as','by'];
// 多级参数命令正则匹配
const multilParams = [/rename<\/span>/, /stats<\/span>/];
/**
* @description 对传入字符串进行正则处理,输出变颜色的字体
* @param {string} html 需要正则处理的字符串
*/
const regHandle = html => {
let temp = html;
// 对蓝色命令进行正则匹配
repCommand.forEach(item => {
temp = temp.replace(
RegExp(`(\\|)([\\s ]*)\\b${item}\\b`, 'g'),
`$1$2<span class="color-blue">${item}</span>`
);
});
// 把字符串以|隔开放入一个数组中,对数组中每一项进行正则处理,实现相应命令参数变色
temp = temp.split('|');
// 对数组每一项进行正则匹配替换颜色
for (let i = 0; i < temp.length; i++) {
// 对橙色as命令进行正则匹配
if (temp[i].match(multilParams[0])) {
temp[i] = temp[i].replace(
RegExp(`\\b${repParamOrange[0]}\\b`, 'g'),
`<span class="color-orange">${repParamOrange[0]}</span>`
);
}
// 对黄色by命令进行正则匹配
if (temp[i].match(multilParams[1])) {
temp[i] = temp[i].replace(
RegExp(`\\b${repParamOrange[1]}\\b`, 'g'),
`<span class="color-orange">${repParamOrange[1]}</span>`
);
}
}
// 把数组转换成字符串输出
return temp.join('|');
};
该函数已经将需要变色的特殊单词做上了标记,将其以span标签包裹。
现在只需要在输入信息的时候进行动态绑定即可:
const oninput = () => {
// 对输入字符串进行颜色转换
show.current.innerHTML = regHandle(hide.current.innerHTML);
}
为什么让两个输入框在滚动和滚动条出现的时候信息同步,需要将两个输入框的scroll同步,大致效果如下:

/**
* @description 保持滑轮同步
*/
const onscroll = () => {
show.current.scrollTop = hide.current.scrollTop;
};
功能开发完成,完整代码如下:
import React, { useRef } from 'react';
import './index.less';
const InputChangeColor = () => {
//命令蓝色
const repCommand = ['rename','stats'];
//命令参数橙色
const repParamOrange = ['as','by'];
// 多级参数命令正则匹配
const multilParams = [/rename<\/span>/, /stats<\/span>/];
// 绑定节点
const hide = useRef(); // 隐藏的输入框的节点
const show = useRef(); // 显示的输入框的节点
/**
* @description 对传入字符串进行正则处理,输出变颜色的字体
* @param {string} html 需要正则处理的字符串
*/
const regHandle = html => {
let temp = html;
// 对蓝色命令进行正则匹配
repCommand.forEach(item => {
temp = temp.replace(
RegExp(`(\\|)([\\s ]*)\\b${item}\\b`, 'g'),
`$1$2<span class="color-blue">${item}</span>`
);
});
// 把字符串以|隔开放入一个数组中,对数组中每一项进行正则处理,实现相应命令参数变色
temp = temp.split('|');
// 对数组每一项进行正则匹配替换颜色
for (let i = 0; i < temp.length; i++) {
// 对橙色as命令进行正则匹配
if (temp[i].match(multilParams[0])) {
temp[i] = temp[i].replace(
RegExp(`\\b${repParamOrange[0]}\\b`, 'g'),
`<span class="color-orange">${repParamOrange[0]}</span>`
);
}
// 对黄色by命令进行正则匹配
if (temp[i].match(multilParams[1])) {
temp[i] = temp[i].replace(
RegExp(`\\b${repParamOrange[1]}\\b`, 'g'),
`<span class="color-orange">${repParamOrange[1]}</span>`
);
}
}
// 把数组转换成字符串输出
return temp.join('|');
};
/**
* @description 保持滑轮同步
*/
const onscroll = () => {
show.current.scrollTop = hide.current.scrollTop;
};
/**
* @description 监听输入框动态绑定
*/
const oninput = () => {
// 对输入字符串进行颜色转换
show.current.innerHTML = regHandle(hide.current.innerHTML);
}
return (
<div className="search">
<div className="show-input" contentEditable="plaintext-only" ref={show} tabIndex="-1" />
<div
className="hide-input"
contentEditable="plaintext-only"
onScroll={onscroll}
onInput={oninput}
ref={hide}
/>
);
};
export default InputChangeColor;
css:
.search {
position: relative;
width: 100%;
height: 40px;
.hide-input {
width: 100%;
padding: 12px 12px 0 12px;
height: auto;
min-height: 39px;
max-height: 70px;
position: absolute;
overflow: auto;
overflow-x: hidden;
color: transparent;
background-color: transparent;
caret-color: black;
box-shadow: 0 0 2px @search-shadow;
}
.show-input {
width: 100%;
padding: 12px 12px 0 12px;
height: auto;
min-height: 39px;
max-height: 70px;
position: absolute;
overflow: auto;
overflow-x: hidden;
background-color:white;
box-shadow: 0 0 2px @search-shadow;
}
.color-blue {
color: blue;
}
.color-orange {
color: orange;
}
}