我得到了一系列“跨度”,需要在 HTML 中的这些索引处添加 div 以“突出显示”该范围的文本。目前,我在要突出显示的文本周围添加 spanStarting spanEnding。后来,我用 .spanStarting/spanEnding 替换了 spanStarting/spanEnding。
跨度如下所示:
{
"begin": 145,
"end": 155
}
Run Code Online (Sandbox Code Playgroud)
只要跨度从不重叠,我就可以工作,现在我需要处理重叠的跨度。例如,重叠跨度如下所示:
{
"begin": 4,
"end": 18
},{
"begin": 4,
"end": 41
}
Run Code Online (Sandbox Code Playgroud)
当跨度重叠时添加 spanStarting/spanEnding 会扭曲索引并导致无法找到要突出显示的正确文本。 你可以在这个fiddle 中看到我到目前为止所拥有的。由于存在重叠跨度,我的代码无法找到正确的索引来放置代码。
我的代码:
String.prototype.replaceBetween = function(start, end, what) {
start = this.substring(0, start);
end = this.substring(end, this.length);
return start + what + end;
};
function createHighlights(subElements, snippet, raw) {
var currentHighlight = subElements;
currentHighlight.spanStart = currentHighlight.begin;
currentHighlight.spanStop = currentHighlight.end;
var currentWord = raw.substring(currentHighlight.spanStart, currentHighlight.spanStop);
currentHighlight.spanStart = snippet.text.indexOf(currentWord);
currentHighlight.spanStop = currentHighlight.spanStart + currentWord.length;
snippet.text = snippet.text.replaceBetween(currentHighlight.spanStart, currentHighlight.spanStop, 'spanStarting' + currentWord + 'spanEnding');
}
var element = {
"text": "The blood pressure was initially elevated on the patient's outpatient medications, so his hypertension medicines were adjusted by increasing his lisinopril to 20 mg qd."
},
rawText = element.text.slice(),
spans = [{
"begin": 145,
"end": 155
}, {
"begin": 4,
"end": 18
}, {
"begin": 4,
"end": 18
}, {
"begin": 90,
"end": 102
}, {
"begin": 4,
"end": 41
}];
spans.forEach(function(currentHighlight) {
if (element.text.indexOf('<span') === -1) {
createHighlights(currentHighlight, element, rawText)
};
})
element.text = element.text.replace(/spanStarting/g, '<span class="highlight">');
element.text = element.text.replace(/spanEnding/g, '</span>');
document.getElementById('text').innerHTML = element.text;
Run Code Online (Sandbox Code Playgroud)
Mat*_*nks 13
显示分层高光的关键之一是首先“展平”您的高光范围集合。这与将 3 维对象正交投影到 2 维平面上没有什么不同。在这种情况下,3 维对象是您的高光层集合。关键是将这些层投影到单个层上,同时保留重叠的数据。如果您可以将所有图层拼合为单个图层,则向用户显示它们会更容易,并且当您尝试在图层周围添加标签时,不会遇到多个图层相互竞争的问题。范围。它还将消除当用户尝试与具有多个堆叠在彼此顶部的部分(例如,使用工具提示)进行交互时可能出现的冲突。
我建议创建一个函数来展平所有高光范围。相交范围可以在数学上简化为范围的平面阵列,并且可以包括有关每个展平部分的合并信息(例如该部分中的交叉点数量以产生更暗的亮点,以及来自该合并的多个交叉点的属性集合范围,例如该范围内的工具提示集合)。
例如,如果您有三个范围
{begin:4,end:18,tooltip:'section 1'}
{begin:4,end:41,tooltip:'section 2'}
{begin:10,end:51,tooltip:'section 3'}
Run Code Online (Sandbox Code Playgroud)
它们将合并为以下结构:
{begin:4,end:10,tooltip:['section 1','section 2'],count:2},
{begin:10,end:18,tooltip:['section 1','section 2','section 3'],count:3},
{begin:18,end:41,tooltip:['section 2','section 3'],count:2}
{begin:41,end:51,tooltip:['section 3'],count:1}
Run Code Online (Sandbox Code Playgroud)
这基本上采用分层范围并将它们展平,同时合并交叉点的数量和属性集合。您会注意到,在新结构中,一切都是顺序的。没有重叠范围(这将消除您在 UI 中遇到的冲突)。您还会注意到原始结构中的所有数据也存在于新结构中。
此外,在展平图层后,您需要对新集合执行一些“特定于 DOM 的”操作。我建议“膨胀”您的范围集合,以便未突出显示的文本部分将放入特殊范围对象(没有突出显示的范围)。这将使执行最后一步变得更容易:将这些范围渲染到 DOM。您还可以在此处仔细检查您的范围,以确保索引对于从零开始的索引字符串正确排列。
这是一个flatten函数,可以将重叠范围的集合展平为“平坦”的范围序列并合并任何额外的数据(例如,工具提示)。
function flattenRanges(ranges) {
var points = [];
var flattened = [];
for (var i in ranges) {
if (ranges[i].end < ranges[i].begin) { //RE-ORDER THIS ITEM (BEGIN/END)
var tmp = ranges[i].end; //RE-ORDER BY SWAPPING
ranges[i].end = ranges[i].begin;
ranges[i].begin = tmp;
}
points.push(ranges[i].begin);
points.push(ranges[i].end);
}
//MAKE SURE OUR LIST OF POINTS IS IN ORDER
points.sort(function(a, b){return a-b});
//FIND THE INTERSECTING SPANS FOR EACH PAIR OF POINTS (IF ANY)
//ALSO MERGE THE ATTRIBUTES OF EACH INTERSECTING SPAN, AND INCREASE THE COUNT FOR EACH INTERSECTION
for (var i in points) {
if (i==0 || points[i]==points[i-1]) continue;
var includedRanges = ranges.filter(function(x){
return (Math.max(x.begin,points[i-1]) < Math.min(x.end,points[i]));
});
if (includedRanges.length > 0) {
var flattenedRange = {
begin:points[i-1],
end:points[i],
count:0
}
for (var j in includedRanges) {
var includedRange = includedRanges[j];
for (var prop in includedRange) {
if (prop != 'begin' && prop != 'end') {
if (!flattenedRange[prop]) flattenedRange[prop] = [];
flattenedRange[prop].push(includedRange[prop]);
}
}
flattenedRange.count++;
}
flattened.push(flattenedRange);
}
}
return flattened;
}
Run Code Online (Sandbox Code Playgroud)
下一步将是inflate扁平化的范围。此功能将填充没有高光的部分,只有一个空的范围结构。它还清理任何可能导致 DOM 重叠的索引。
function inflateRanges(ranges, length=0) {
var inflated = [];
var lastIndex;
for (var i in ranges) {
if (i==0) {
//IF THERE IS EMPTY TEXT IN THE BEGINNING, CREATE AN EMOTY RANGE
if (ranges[i].begin > 0){
inflated.push({
begin:0,
end:ranges[i].begin-1,
count:0
});
}
inflated.push(ranges[i]);
} else {
if (ranges[i].begin == ranges[i-1].end) {
ranges[i-1].end--;
}
if (ranges[i].begin - ranges[i-1].end > 1) {
inflated.push({
begin:ranges[i-1].end+1,
end:ranges[i].begin-1,
count:0
});
}
inflated.push(ranges[i]);
}
lastIndex = ranges[i].end;
}
//FOR SIMPLICITY, ADD ANY REMAINING TEXT AS AN EMPTY RANGE
if (lastIndex+1 < length-1) {
inflated.push({
begin:lastIndex+1,
end:length-1,
count:0
})
}
return inflated;
}
Run Code Online (Sandbox Code Playgroud)
最后,我建议将每个范围内的实际文本也添加到每个相应的范围内。这不是绝对必要的,但它使测试更容易。
function fillRanges(ranges, text) {
for (var i in ranges) {
ranges[i].text = text.slice(ranges[i].begin,ranges[i].end+1);
}
return ranges;
}
Run Code Online (Sandbox Code Playgroud)
我还创建了一个工作示例,使用您问题中的确切数据。我确实添加了一个名为的附加属性tooltip来说明如何使用合并的属性。此外,我还添加了一些简单的tooltipCSS 来展示工具提示如何与分层范围一起使用的实际示例。
这是工作示例:https : //jsfiddle.net/mspinks/shfpxp82/