Javascript 突出显示和编辑长字符串中的文本
在HTML/JavaScript/React/Redux web应用程序中,我有一个自然语言的长字符串(大约300kb)。这是正在播放的一段录音的誊本 我需要Javascript 突出显示和编辑长字符串中的文本,javascript,html,performance,optimization,Javascript,Html,Performance,Optimization,在HTML/JavaScript/React/Redux web应用程序中,我有一个自然语言的长字符串(大约300kb)。这是正在播放的一段录音的誊本 我需要 要突出显示当前说出的单词 要识别单击的单词 提取选定范围的步骤 以及替换字符串的部分内容(当用户提交对成绩单的更正时) 当我把每个单词都用自己的包装起来时,一切都很简单。然而,这使得浏览器无法忍受元素的数量,页面变得非常缓慢 我可以想出两种方法: 我可以用来包装每个句子,并且只包装当前播放的句子中的每个单词 我可以让文本不带HTML
- 要突出显示当前说出的单词
- 要识别单击的单词
- 提取选定范围的步骤
- 以及替换字符串的部分内容(当用户提交对成绩单的更正时)
包装起来时,一切都很简单。然而,这使得浏览器无法忍受元素的数量,页面变得非常缓慢
我可以想出两种方法:
- 我可以用
来包装每个句子,并且只包装当前播放的句子中的每个单词 - 我可以让文本不带HTML标记,通过
处理单击,但我不知道如何突出显示单词document.caretPositionFromPoint
O(logn)
中运行,而不是O(n)
见:
CSS
使用带有透明彩色文本的覆盖图,这样我们可以使覆盖图与单词具有相同的宽度
#overlay {
background-color: yellow;
opacity: 0.4;
display: block;
position: absolute;
color: transparent;
}
下面是完整的分叉JavaScript代码
var overlayDom = document.getElementById('overlay');
function findClickedWord(parentElt, x, y) {
if (parentElt.nodeName !== '#text') {
console.log('didn\'t click on text node');
return null;
}
var range = document.createRange();
var words = parentElt.textContent.split(' ');
var start = 0;
var end = 0;
for (var i = 0; i < words.length; i++) {
var word = words[i];
end = start+word.length;
range.setStart(parentElt, start);
range.setEnd(parentElt, end);
// not getBoundingClientRect as word could wrap
var rects = range.getClientRects();
var clickedRect = isClickInRects(rects);
if (clickedRect) {
return [word, start, clickedRect];
}
start = end + 1;
}
function isClickInRects(rects) {
for (var i = 0; i < rects.length; ++i) {
var r = rects[i]
if (r.left<x && r.right>x && r.top<y && r.bottom>y) {
return r;
}
}
return false;
}
return null;
}
function onClick(e) {
var elt = document.getElementById('info');
// Get clicked status
var clicked = findClickedWord(e.target.childNodes[0], e.clientX, e.clientY);
// Update status bar
elt.innerHTML = 'Nothing Clicked';
if (clicked) {
var word = clicked[0];
var start = clicked[1];
var r = clicked[2];
elt.innerHTML = 'Clicked: ('+r.top+','+r.left+') word:'+word+' at offset '+start;
// Update overlay
overlayDom.innerHTML = word;
overlayDom.style.top = r.top + 'px';
overlayDom.style.left = r.left + 'px';
}
}
document.addEventListener('click', onClick);
var overlayDom=document.getElementById('overlay');
函数findClickedWord(parentElt,x,y){
如果(parentElt.nodeName!='#text'){
log('没有单击文本节点');
返回null;
}
var range=document.createRange();
var words=parentElt.textContent.split(“”);
var start=0;
var-end=0;
for(var i=0;i
请参阅forked演示:
此实现使用“识别单击的单词”
新答案
我想,我之前回答的代码实际上必须在每次点击事件中将巨大的文本字符串分割成一个巨大的数组。然后,对数组执行线性搜索以定位匹配字符串
然而,这可以通过预先计算单词数组并使用二进制搜索而不是线性搜索来改进。
现在,每个突出显示都将在O(logn)
中运行,而不是O(n)
见:
CSS
使用带有透明彩色文本的覆盖图,这样我们可以使覆盖图与单词具有相同的宽度
#overlay {
background-color: yellow;
opacity: 0.4;
display: block;
position: absolute;
color: transparent;
}
下面是完整的分叉JavaScript代码
var overlayDom = document.getElementById('overlay');
function findClickedWord(parentElt, x, y) {
if (parentElt.nodeName !== '#text') {
console.log('didn\'t click on text node');
return null;
}
var range = document.createRange();
var words = parentElt.textContent.split(' ');
var start = 0;
var end = 0;
for (var i = 0; i < words.length; i++) {
var word = words[i];
end = start+word.length;
range.setStart(parentElt, start);
range.setEnd(parentElt, end);
// not getBoundingClientRect as word could wrap
var rects = range.getClientRects();
var clickedRect = isClickInRects(rects);
if (clickedRect) {
return [word, start, clickedRect];
}
start = end + 1;
}
function isClickInRects(rects) {
for (var i = 0; i < rects.length; ++i) {
var r = rects[i]
if (r.left<x && r.right>x && r.top<y && r.bottom>y) {
return r;
}
}
return false;
}
return null;
}
function onClick(e) {
var elt = document.getElementById('info');
// Get clicked status
var clicked = findClickedWord(e.target.childNodes[0], e.clientX, e.clientY);
// Update status bar
elt.innerHTML = 'Nothing Clicked';
if (clicked) {
var word = clicked[0];
var start = clicked[1];
var r = clicked[2];
elt.innerHTML = 'Clicked: ('+r.top+','+r.left+') word:'+word+' at offset '+start;
// Update overlay
overlayDom.innerHTML = word;
overlayDom.style.top = r.top + 'px';
overlayDom.style.left = r.left + 'px';
}
}
document.addEventListener('click', onClick);
var overlayDom=document.getElementById('overlay');
函数findClickedWord(parentElt,x,y){
如果(parentElt.nodeName!='#text'){
log('没有单击文本节点');
返回null;
}
var range=document.createRange();
var words=parentElt.textContent.split(“”);
var start=0;
var-end=0;
for(var i=0;i
请参阅forked演示:
这个实现
span {
background-color: #B6B6B4;
}