2013-09-05 30 views
2

我正在嘗試使用HTML 定義列表,作爲帶有工具提示的詞彙表。有關預期結果,請參閱this fiddle(底部)。查找發生 - 尚未包含在A標記中 - 由A標記包圍

如果文章中的部分文本與定義列表中的條款匹配,則會顯示工具提示氣泡並顯示匹配的定義。這是由周圍的A標籤完成的。到現在爲止還挺好。

現在的問題是如何解析文章,以便可以在術語表中找到的所有部分將自動被A-tags包圍。此項目可以是例如:

  • 社會團體
  • 羣體行爲
  • 研究

解析必須是「貪婪」,所以它首先解析最長的定義。

var arr = []; /* Array of terms, sorted by length */ 
$('#glossary dt').each(function() { arr.push($(this).text()); }); 
arr.sort(function(a,b) { return b.length - a.length; }); 

接着,它具有由標籤包圍的第一陣列項出現在#article.html,除非他們已經是一個標籤內。這也應該是不區分大小寫的。

/* don't know how to approach this */ 

最後移動到下一個數組項並重復。

/* ok, i can figure the loop out myself */ 

我的問題是檢查是否某些字符串已經是一個標籤內,並與周圍放置文本的一部分的標籤。應避免更換以保持大寫/小寫相同。檢查JSfiddle的預期結果。


編輯:下面plalx的解決方案構建,我想出了下面的代碼:

/* 
This script matches text strings in an article with terms in a defintion list (DL); 
the DL acts as a glossary. The text strings are wrapped in '<span>' tags, 
with the 'title' attribute containing the definition. This allows for easy custom tooltips. 

- This script is 'greedy' the longest terms are matched first; 
- This script preserves capitalization and escapes HTML in the definitions 
*/ 
var $article = $('#container-inhoud'); 
var $terms = $('#glossary dt'); 
//clone to avoid multiple DOM reflows 
var $clone = $article.clone(); 

//create a regex that matches all glossary terms (not case-sensitive), sorted by length 
var rx = new RegExp('\\b(' + $terms.map(function() { 
      return $(this).text(); 
     }).get().sort(function (term1, term2) { 
        return term2.length - term1.length; 
       }).join('|') + ')\\b', 'ig'); 


var entityMap = { 
    "&": "&amp;", 
    "<": "&lt;", 
    ">": "&gt;", 
    '"': '&quot;', 
    "'": '&#39;', 
    "/": '&#x2F;' 
}; 

function escapeHtml(string) { 
    return String(string).replace(/[&<>"'\/]/g, function (s) { 
     return entityMap[s]; 
    }); 
} 

//wrap any text string that corresponds with a definition term in a 'span' tag 
//the title contains the connected definition. 
function replacer(match){ 
     var definition = $terms.filter(function() { 
      return $(this).text().toLowerCase() == match.toLowerCase(); 
     }).next('dd').text(); 
     definition = escapeHtml(definition); 
     return '<span class="tooltip" title="' + definition + '">' + match + '</span>'; 
} 

//call the replace function for every regex match 
$clone.html($clone.html().replace(rx , replacer)); 

//unwrap the terms in the glossary section (to avoid tooltips within the glossary itself) 
//only needed if the #glossary is within the #article container, otherwise delete the next line. 
$clone.find('#glossary .tooltip').contents().unwrap(); 

$article.replaceWith($clone); 

下面的代碼顯示工具提示。

/* 
This script displays a tooltip for every 'span' with class 'tooltip'. 
The 'title' attribute will be displayed as the tooltip. 
The tooltip is displayed directly above and in line with the left side of the element. 
Any offsetting is done by manipulation the 'left' and 'top' attributes in the CSS. 
*/ 
$("span.tooltip").hover(
     function() { 
      var bubble = $("#bubble"); 
      bubble.text($(this).attr('title')); 
      var ypos = $(this).offset().top - bubble.height(); 
      var xpos = $(this).offset().left; 
      bubble.css({"left":xpos+"px","top":ypos+"px"}); 
      bubble.show(); 
     }, 
     function() { 
      $("#bubble").hide(); 
     } 
); 

我用下面的CSS格式化工具提示氣泡:

#bubble{ 
      -webkit-border-radius: 4px; 
      -moz-border-radius: 4px; 
      border-radius: 4px; 
      border: 1px solid #888; 
      color: #ee6c31; 
      background-color: rgba(255, 255, 255, 0.85); 
      position:absolute; 
      z-index:200; 
      padding:5px 8px; 
      margin: -15px 5px 5px -10px; 
      display:none; 
     } 
+1

看一看http://stackoverflow.com/questions/15958020/variable-inside-一個字符串在一個字符串/ 15959797#15959797 – plalx

+0

謝謝,試圖包裹我的頭,現在。 –

+0

如果持有該文章內容的節點的父元素不包含具有直接附加到其中的行爲的任何其他元素,則可以使用answer中描述的easy regex替換解決方案。如果行爲是使用委託來附加的,則不應該由於替換文章容器的'innerHTML'值而引起問題。 – plalx

回答

2

我爲你制定了一個小解決方案。我認爲這是你想要什麼:

HTML樣本:

<div id="glossary"> 
    <dt>test</dt> 
    <dt>Testing</dt> 
    <dt>something</dt> 
    <dt>some other thing</dt> 
    <dt>long test</dt> 
</div> 

<div id="article"> 
    this is a test, to see if Testing will correctly be wrapped by something, or some other thing. 
    This <a data-glossary-term href="#test">test</a> should not be wrapped again however, <span>something</span> should! 
    Very long test should also be wrapped first. 
</div> 

JS:

$(function() { 
    var $article = $('#article'), 
     //clone to avoid multiple DOM reflows 
     $clone = $article.clone(), 
     //create a regex that matches all glossary terms (not case-sensitive) 
     rx = new RegExp('\\b(' + $('#glossary dt').map(function() { 
      return $(this).text(); 
     }).get().sort(function (term1, term2) { 
      return term2.length - term1.length; 
     }).join('|') + ')\\b', 'ig'); 

    //unwrap previously wrapped glossary terms to avoid issues 
    //this would not be needed if your initial markup doesn't have terms wrapped already 
    $clone.find('[data-glossary-term]').each(function() { 
     $(this).replaceWith(this.childNodes); 
    }); 

    //wrap terms in the 'a' tag 
    $clone.html($clone.html().replace(rx, '<a data-glossary-term="$1" href="#$1">$1</a>')); 

    $article.replaceWith($clone); 
}); 
+0

仍然必須檢查一個,但它有這麼多的美麗。正如你可能收集的一樣,我對JavaScript很陌生,但你看起來很好。 –

+0

就像我的魅力一樣,但我必須閱讀RegEx才能完全掌握它。我必須努力的一些東西:'替換'的問題是它不會尊重原始大寫。想象一下,一個句子的開頭是:'Something'>這會被'something'取代。現在A標籤也將取代之前的A標籤。即使原始A標籤跨越較長的一段文字,其中包含一個詞語。對我來說不是問題,但將行爲設爲可選將會很好。 Thx再次投入努力。 –

+0

@snirp基本上,當您修改DOM元素時,會導致[DOM重排](https://developers.google.com/speed/articles/reflow?hl=fr)在這裏,我克隆了初始節點以使用元素排除在文檔流之外,以避免在刪除初始數據詞彙表術語時重複多次迴流,然後更改元素的innerHTML屬性。如果您不必刪除最初的詞彙表術語,則不必使用克隆,因爲將執行的唯一操作是更改'innerHTML'屬性,導致單個迴流。 – plalx

0

代替收集dt的文本到陣列,收集DOM項本身,然後創建這樣的功能:

function add_tags(td) { 
    var dd = td.next(), 
     q = td.text() 
     txt = dd.text().replace(q, '<a href="#">'+q+'</a>'); 

    dd.html(txt); 
}; 

並循環它。你可能想添加一些檢查功能,看看周圍是否已經有任何標籤,但這是一個很好的起點

+0

我想我需要有數組,因爲我需要它首先按長度排序。 正如我所說的,一個簡單的replace()可能不適用於大寫字母。例如,我希望「研究」與術語「研究」相匹配,但不能用小寫代替。 在你的例子中,A標籤的檢查函數應該放在replace()方法的第二個參數中,對吧? –

+0

我沒有說完全刪除數組我說不建立一個數組的td文本,而是td本身。然後根據文本的長度對其進行排序並循環使用我的功能。這只是一個通用的例子,而不是一個完整的解決方案。我試着告訴你你不知道如何接受 – yuvi

+0

好吧,我明白你的意思。不過,我似乎有這部分工作。也許我的解決方案在那裏是次優的。 –