我使用this简单库来创建文本叠加层,如下所示
匹配文字,将css叠加在上面:
new Textoverlay(document.getElementById("textarea"), [
{
match: /\B@\w+/g,
css: { "background-color": "#d8dfea" }
},
{
match: /e\w{8}d/g,
css: { "background-color": "#cc9393" }
}
]);
这是使用基于Regex的方法:
export interface Matcher {
lastIndex: number;
exec: (input: string) => null | [string] | RegExpExecArray;
}
作为匹配策略:
export interface Strategy {
match: Matcher;
css: CssStyle;
}
这没关系,但我需要不同的东西,我无法摆脱。在我的情况下,我需要匹配文本中的标记列表,我已经在输入文档中使用char begin和end offset,如:
[
{
"text": "hello",
"charBegin": 0,
"charEnd": 4
},
{
"text": "how",
"charBegin": 6,
"charEnd": 8
} ,
{
"text": "are",
"charBegin": 9,
"charEnd": 11
}
]
所以我的情况是这种方法就像
一样new Textoverlay(textarea, [
{
match: [0,4],
css: { 'background-color': '#d8dfea' },
},
{
match: [6,8],
css: { 'background-color': '#cc9393' },
},
{
match: [9,11],
css: { 'background-color': '#ddd' },
}
]);
所以喜欢使用基于范围的匹配器,因为有些用户建议我喜欢
export class RangeMatcher {
public start: number;
public end: number;
public lastIndex: number;
constructor(start: number, end: number) {
this.start = start;
this.end = end;
this.lastIndex = -1;
}
public exec(text: string) {
if (this.lastIndex !== -1) {
this.lastIndex = -1;
return null;
}
this.lastIndex = this.start;
return [text.slice(this.start, this.end)];
}
}
问题是我无法摆脱文本节点解析here
private computeOverlayNodes(): Node[] {
return this.strategies.reduce((ns: Node[], strategy) => {
const highlight = document.createElement('span');
setStyle(highlight, strategy.css);
return Array.prototype.concat.apply([], ns.map((node) => {
if (node.nodeType !== Node.TEXT_NODE) {
return node;
}
const text = node.textContent || '';
const resp = [];
while (true) {
const prevIndex = strategy.match.lastIndex;
const match = strategy.match.exec(text);
if (!match) {
resp.push(document.createTextNode(text.slice(prevIndex)));
break;
}
const str = match[0];
resp.push(document.createTextNode(
text.slice(prevIndex, strategy.match.lastIndex - str.length)));
const span = highlight.cloneNode();
span.textContent = str;
resp.push(span);
}
return resp;
}));
}, [document.createTextNode(this.textarea.value)]);
}
因为在这个方法中我没有文档中文本节点的绝对位置,节点本身会累积前面的文本。