javascript - 在给定开始和结束索引处使用 javascript 在后台创建文本突出显示
问题描述
在使用列表索引突出显示一个 div 中的文本时,我发现了一些困难
例如,我有类似于下面的文本 div:
<div> <!--Highlight at given index in this div -->
<p>As a friend of Romeo’s, Mercutio supports the Montague’s in the ancient feud. An example of Mercutio defending the Montague’s is when Tybalt, a member of the loathed Capulet family, abuses Romeo and Mercutio intervenes on Romeo’s behalf. Attempting to restore peace, Romeo gets between the two combatants and Mercutio “hath got his mortal hurt” (Page 149; Act 3, Scene 1) on Romeo’s account. In spite of his “life shall pay the forfeit of peace” (page 17; Act 1, Scene 1), Romeo seeks revenge on Tybalt as he loves his murdered friend. As Romeo kills Tybalt out of love for Mercutio, Shakespeare suggests that love conquered the thought of being penalized with death.</p>
<p>Want to overlap this highlight too</p>
<p>Want to overlap this highlight too</p>
</div>
HTML 转义版本如下所示:
As a friend of Romeo’s, Mercutio supports the Montague’s in the ancient feud. An example of Mercutio defending the Montague’s is when Tybalt, a member of the loathed Capulet family, abuses Romeo and Mercutio intervenes on Romeo’s behalf. Attempting to restore peace, Romeo gets between the two combatants and Mercutio “hath got his mortal hurt” (Page 149; Act 3, Scene 1) on Romeo’s account. In spite of his “life shall pay the forfeit of peace” (page 17; Act 1, Scene 1), Romeo seeks revenge on Tybalt as he loves his murdered friend. As Romeo kills Tybalt out of love for Mercutio, Shakespeare suggests that love conquered the thought of being penalized with death.
Want to overlap this highlight too
Want to overlap this highlight too
我有一个亮点列表,如下所示:
[
{
"id": "cb8c8875-fba4-4abd-9cca-9cc304b9cba6",
"start_index": 179,
"end_index": 184,
"color": "green",
},
{
"id": "9698dd27-ed20-4824-82b0-1548016b5839",
"start_index": 6,
"end_index": 53,
"color": "yellow",
]
我尝试了什么:
我想知道 Javascript Range对象用于根据给定索引创建范围。这将返回一个可用于获取边界矩形的范围。但是没有知道如何从索引中制作那个Range对象。
我想要一个 javascript 代码在给定的文本索引(start_index和end_index)处添加绝对定位的突出显示 div,如下图所示。
请在下面找到示例输出。
任何帮助,将不胜感激。
如果需要更多信息,请发表评论。
注意和更新:
使用
getBoundingClientRect
orgetClientRects()
获取绝对位置div 中的文本不是静态的,它可能会有所不同。
遍历亮点并使用开始和结束索引创建字符跨度,并根据颜色将合适的类名添加到这些跨度
div 可能包含斜体和粗体文本
解决方案
免责声明:
我们在跨平台文档查看器 React 应用程序中实现了文本突出显示,用户可以通过触摸添加突出显示,我们在他们下次查看文档时加载它们。不用说,它超级复杂。
最困难的问题是从渲染文本中获取文本位置。我假设您也不必处理该部分,否则答案可能会变得很长。三位主要开发人员投入了几天的时间来获得一个可行的解决方案,因此请注意。
重要的是要认识到源代码中的文本位置(例如,您的数据库中的 HTML 文本)、呈现的(浏览器的node.innerHTML
)和可见文本是不同的。
在某些情况下,浏览器甚至会插入虚拟节点。如果您需要从一个转换到另一个,您首先必须标准化位置,例如通过遍历内容node.innerHTML
并忽略标签、多个空格和换行符。
解决方案:
在文本上方放置框很难实现(跨浏览器),并且不能很好地处理多行文本或调整内容区域的大小。
另一方面,当突出显示在一个标签上下文中开始并在另一个标签上下文中结束时,将标签插入文本不会直接起作用,例如,当您有子标签时<p>Hello <b>World Wide Web</b></p>
,您想要突出显示“Hello World”。
诀窍是首先在开始和结束位置插入空体标记标签,然后在第二步中添加高光。当你这样做时,向后浏览内容很重要,因为插入会改变位置。
步骤1
sourceContent
在下面的代码中可以是parentNode.innerHTML
,但如果你必须使用它,位置可能会由于 HTML 标签、空格和换行符而偏移。因此,如果您有源内容(例如来自数据库),请使用它!
insertHighlightMarkerTags(sourceContent = '', highlights = []) {
const arrSortByIdx = [];
// collect marker locations for start and end of highlights
highlights.forEach((record) => {
arrSortByIdx.push({
start: true,
idx: record.startIndex,
record,
});
arrSortByIdx.push({
end: true,
idx: record.endIndex,
record,
});
});
let processedContent = sourceContent;
// sort marker location in reverse order, so we can insert them without messing with the index positions
arrSortByIdx.sort((a, b) => b.idx - a.idx);
// insert marker locations in reverse order
arrSortByIdx.forEach((item) => {
const tagName = item.start ? 'mon' : 'moff';
const tag = `<${tagName} data-id="${item.record.id}"></${tagName}>`;
processedContent = processedContent.slice(0, item.idx) + tag + processedContent.slice(item.idx);
});
return processedContent;
},
第2步
现在,我们将位置作为内容中的标签。它们仅用作标记,不会显示。但是它们允许我们通过查询它们来创建范围:
getRangeFor(domNode, record) {
const range = document.createRange();
const fromElem = domNode.querySelector(`mon[data-id="${record.id}"]`);
const toElem = domNode.querySelector(`moff[data-id="${record.id}"]`);
if (fromElem === null || toElem === null) {
// preselect the whole page and then reduce the selection, if necessary
range.selectNodeContents(domNode);
}
if (fromElem) {
range.setStartAfter(fromElem);
}
if (toElem) {
range.setEndBefore(toElem);
}
return range;
}
第 3 步
现在可以使用出色的wrap-range-text库将文本包装到标记标签中。(有许多类似的库,但由于 Edge 和 IE 中的奇怪错误,大多数都不能跨浏览器工作):
import wrapRange from 'wrap-range-text';
createMarker(range, highlight, start = false) {
const wrapper = document.createElement('mark');
wrapper.setAttribute('style', `background-color: ${highlight.color}`);
wrapper.setAttribute('data-id', record.id);
wrapper.setAttribute('class', className);
wrapRange(wrapper, range);
},
推荐阅读
- ruby-on-rails - 在 Rails Admin 中使用具有多态关联的命名空间模型
- sas - " 将数据导入 SAS 时的字符
- c# - 是否可以在 MVC 中为 ViewBag 分配 ID?
- java - 将数据存储到 GlobalKTable
- c++ - 标准库是否有办法检查两个模板类型的基本模板类型是否相等?
- angular - 打字稿库:客户端巨大文件的增量散列
- docker - 并行运行多组 docker 服务
- excel - Excel条件公式
- javascript - 渲染没有返回任何内容;试图重定向到主页
- javascript - 将 CanvasTexture 作为地图应用到导入的 .obj 模型