gpt4 book ai didi

javascript - 突出显示和编辑长字符串中的文本

转载 作者:行者123 更新时间:2023-11-28 04:46:40 25 4
gpt4 key购买 nike

在 HTML/JavaScript/React/Redux 网络应用程序中,我有一个很长的自然语言字符串(大约 300kb)。它是正在播放的录音的转录本。

我需要

  • 突出显示当前说出的单词,
  • 识别被点击的单词,
  • 提取选定的范围
  • 并替换部分字符串(当用户提交对抄本的更正时)。

当我将每个单词包装在自己的 <span> 中时,一切都变得简单了.然而,这使得浏览器无法承受元素的数量并且页面变得非常慢。

我可以想到两种方法来解决这个问题:

  • 我可以将每个句子包装在 <span> 中并且只包装当前播放的句子的每个单词。

  • 我可以让文本不带 HTML 标签,通过 document.caretPositionFromPoint 处理点击, 但我不知道如何突出显示一个词。

我欢迎更多关于难度和速度之间平衡的想法和想法。

最佳答案

“识别被点击的单词”

新答案

我认为,我之前的回答中的代码实际上必须在每次点击事件时将巨大的文本字符串拆分为一个巨大的数组。然后对数组进行线性搜索,找到匹配的字符串。

但是,这可以通过预先计算单词数组并使用二进制搜索而不是线性搜索来改进。现在每个突出显示都将在 O(log n) 而不是 O(n)

中运行

参见:http://jsfiddle.net/amoshydra/vq8y8h19/

// Build character to text map
var text = content.innerText;

var counter = 1;
textMap = text.split(' ').map((word) => {
result = {
word: word,
start: counter,
end: counter + word.length,
}
counter += word.length + 1;
return result;
});

content.addEventListener('click', function (e) {
var selection = window.getSelection();
var result = binarySearch(textMap, selection.focusOffset, compare_word);
var textNode = e.target.childNodes[0];

if (textNode) {
var range = document.createRange();
range.setStart(textNode, textMap[result].start);
range.setEnd(textNode, textMap[result].end);
var r = range.getClientRects()[0];
console.log(r.top, r.left, textMap[result].word);

// Update overlay
var scrollOffset = e.offsetY - e.clientY; // To accomondate scrolling
overlay.innerHTML = textMap[result].word;
overlay.style.top = r.top + scrollOffset + 'px';
overlay.style.left = r.left + 'px';
}
});

// Slightly modified binary search algorithm
function binarySearch(ar, el, compare_fn) {
var m = 0;
var n = ar.length - 1;
while (m <= n) {
var k = (n + m) >> 1;
var cmp = compare_fn(el, ar[k]);
if (cmp > 0) {
m = k + 1;
} else if(cmp < 0) {
n = k - 1;
} else {
return k;
}
}
return m - 1;
}

function compare_word(a, b) {
return a - b.start;
}

原始答案

我从这个 answer from aaron 中提取了一个代码分支并实现了这个:

除了在段落上设置 span 标签,我们可以在单词之上放置一个覆盖层。
并在移动到一个词时调整叠加层的大小和位置。

片段

JavaScript

// Update overlay
overlayDom.innerHTML = word;
overlayDom.style.top = r.top + 'px';
overlayDom.style.left = r.left + 'px';

CSS

使用带有透明颜色文本的叠加层,这样我们就可以使叠加层与文字宽度相同。

#overlay {
background-color: yellow;
opacity: 0.4;
display: block;
position: absolute;
color: transparent;
}

下面是完整的 fork 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);

查看 fork 演示:https://jsfiddle.net/amoshydra/pntzdpff/

此实现使用 createRange API

关于javascript - 突出显示和编辑长字符串中的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43052727/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com