2016-10-18 7 views
0

See Fiddle重複テキストを作成するjavascriptの

にまたがる私は「スパン」のシリーズを与えられ、「ハイライト」のテキストの範囲にするために、これらの指標でHTMLにdivを追加する必要がありますしています。現在、強調したいテキストの周りにspanStarting spanEndingを追加しています。その後、私はspanStarting/spanEndingをで置き換えます。私はこのように長いスパンが、今私は、重複スパンに対処する必要があり、重複したことがないとして働いてきた

{ 
"begin": 145, 
"end": 155 
} 

スパンは次のようになります。例えばは、重複スパンは次のようになります。

{ 
"begin": 4, 
"end": 18 
},{ 
"begin": 4, 
"end": 41 
} 

はスパンが重複している場合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; 

答えて

2

一つは、最初にある「フラット化」のハイライト範囲のあなたのコレクション。これは、3次元オブジェクトを2次元平面上に正射影することとは異なりません。この場合の3次元オブジェクトは、ハイライトレイヤのコレクションです。重要な点は、重複したデータを維持しながら、これらのレイヤーを単一のレイヤーに投影することです。すべてのレイヤーを1つのレイヤーにまとめることができれば、それらをユーザーに表示する方が簡単になります。また、レイヤーを囲むタグを追加しようとすると、複数のレイヤーが互いに競合するという問題に遭遇することはありません範囲。また、ユーザーが複数のレイヤーを重ねて(たとえば、ツールチップを使用して)セクションを操作しようとしたときに発生する可能性のある競合を削除します。

すべてのハイライト範囲を平坦にする関数を作成することをお勧めします。交差範囲は数学的に範囲のフラットな配列に縮小することができ、各平坦化されたセクションについての統合された情報を含めることができます(そのセクション内のより暗いハイライトを生成するための交差点の数や、その範囲内にあるツールチップの集合など)。例えば

、あなたは3つの範囲

{begin:4,end:18,tooltip:'section 1'} 
{begin:4,end:41,tooltip:'section 2'} 
{begin:10,end:51,tooltip:'section 3'} 

を持っている場合、彼らはこのような構造に統合されます。

{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} 

は、これは本質的に層状の範囲を取り、交差点の数の両方を統合しながら、それらを平坦化しますおよび属性の集合。あなたは、新しい構造ではすべてが逐次的であることに気付くでしょう。オーバーラップ範囲はありません(UIで発生している競合を解消します)。また、元の構造からのすべてのデータが新しい構造にも存在することに気づくでしょう。

さらに、レイヤを平坦化した後、新しいコレクションにいくつかの「DOM固有の」ものを行う必要があります。範囲のコレクションを「膨らませる」ことをお勧めします。そのため、ハイライトされていないテキストのセクションは、特殊な範囲オブジェクト(ハイライトのない範囲)に配置されます。これにより、最終ステップをより簡単に実行できるようになります。これらの範囲をDOMにレンダリングします。ここで範囲を再確認して、0から始まるインデックス文字列に対してインデックスが正しく並んでいることを確認することもできます。

オーバーラップ範囲の集まりを「フラット」な範囲のシーケンスに平坦化し、余分なデータ(ツールチップなど)を統合する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; 
} 

次のステップは、平坦化された範囲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; 
} 

最後に、これらの範囲内にある実際のテキストをそれぞれ対応する範囲に追加することをおすすめします。これは絶対に必要なわけではありませんが、テストが容易になります。

function fillRanges(ranges, text) { 
    for (var i in ranges) { 
    ranges[i].text = text.slice(ranges[i].begin,ranges[i].end+1); 
    } 
    return ranges; 
} 

また、質問の正確なデータを使用して、実際の例を作成しました。統合プロパティをどのように使用できるかを示すために、tooltipという追加のプロパティを追加しました。また、単純なtooltip CSSを追加して、階層化された範囲でツールチップがどのように使用されるかの実例を紹介しました。提案のためのhttps://jsfiddle.net/mspinks/shfpxp82/

+0

ありがとう:ここ

は実施例です。このソリューションの私の問題は、将来、各スパンのツールチップを追加する必要があるかもしれないし、スパンの重複を示すために異なる色付けをする必要があるかもしれないということです。 – Mcestone

+0

異なる色の交差する2つのスパンがあるとどうなりますか?交差点はどのような色ですか?また、ツールチップとスパンを交差させてハイレベルと話している場合、どの交差点にカーソルを置いたときにどのツールチップが表示されるかを見極めるにはどうしますか? –

+0

annotator.jsが重複するスパンを処理する方法は、http://annotatorjs.org/の理想的なソリューションです。基本的には、ハイライト(明るい黄色)のテキストの大きな文字列を持つことができます。このテキストには、ホバー上で表示できるツールチップがあります。大きな文字列内には強調表示されている単語が1つあります。これは暗い黄色で、独自のツールチップがあります。 1つの単語にカーソルを置くと、1つの単語のヒントと親のテキストのヒントが表示されます。 – Mcestone

0
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 
    }]; 

var flags = new Array(); 
for (i = 0; i < spans.length; i++) { 
    for (j = spans[i].begin; j <= spans[i].end; j++) { 
    flags[j] = true; 
    } 
} 

isSpan = false 
var starts = new Array(); 
var ends = new Array(); 
for (i = 0; i < flags.length; i++) { 
    if (flags[i]) { 
    if (!isSpan) { 
     starts.push(i); 
    } 
    isSpan = true; 
    } 
    else { 
    if (isSpan) { 
     ends.push(i); 
    } 
    isSpan = false; 
    } 
} 
if (flags.length > 0 && ends.length < starts.lenght) { 
    ends.push(flags.length - 1); 
} 

var newSpans = new Array(); 
for (i = 0; i < starts.length; i++) { 
    newSpans.push({"begin": starts[i], "end": ends[i]}); 
} 

newSpans.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; 
関連する問題