I have a string like this:
"Size: 40; Color: 30"
I want to create tooltips for them such that it looks like this:
<span class='tooltip' data-tooltip='The Size of a Unit is controlled by the Color of the Unit.'>Size</span>: 40; <span class='tooltip' data-tooltip='The Color of a Unit is a standard Setting.'>Color</span>: 30
Using a naive replacement however I end up with this:
<span class='tooltip' data-tooltip='The Size of a Unit is controlled by the <span class='tooltip' data-tooltip='The Color of a Unit is a standard Setting.'>Color</span> of the Unit.'>Size</span>: 40; <span class='tooltip' data-tooltip='The Color of a Unit is a standard Setting.'>Color</span>: 30
Which is not what I want. How do I write a regex or do a replacement in such a way that it doesn't replace text that's already part of the tooltip?
Edit: I didn't make it clear that the replacements are not Size and Color, they're just examples. I'm adding an arbitrary amount, usually 20+ tooltips to any string.
Here are some testables:
var tooltips = {
"Size":"The Size of a Unit is controlled by the Color",
"Color": "bar",
"Time and Size": "foo"
}
"Here we have something of <b>Size</b> 20 and Color red. it's very important that the Time and Size of the work and kept in sync."
Should result in:
"Here we have something of <b><span class='tooltip' data-tooltip='The Size of a Unit is controlled by the Color'>Size<span></b> 20 and <span class='tooltip' data-tooltip='bar'>Color<span> red. it's very important that the <span class='tooltip' data-tooltip='foo'>Time and Size<span> of the work and kept in sync."
The longer match should take precedence over shorter matches. It should match on only whole words and not parts of words.
Edit: Forgot to state yet another requirement.
It should still match strings that are wrapped with tags that are not tooltips.
Via data attributes − To add a tooltip, add data-toggle = "tooltip" to an anchor tag. The title of the anchor will be the text of a tooltip. By default, tooltip is set to top by the plugin.
The title attribute on a <td> tag adds a tooltip with title text to the table data cell. Hovering the mouse over the table cell will display the tooltip.
As we mentioned in the preamble, tooltips are completely "mute" to any interactions as to not steal hover events from elements underneath them. Even placing interactive elements, such as links, into content of tooltip will not work.
I think a single str.replace will do the work if got the right regexp pattern.
function replaceTooltips(str, tooltips) {
//copy from https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
function escapeRegExp(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
var keys = [];
for ( var k in tooltips) {
keys.push(escapeRegExp(k));
}
//create a regexp like (Size)|(Color)|(Time and Size)
var ptn = new RegExp('(' + keys.join(')|(') + ')', 'g');
str = str.replace(ptn, function(mat) {
if (!tooltips[mat]) {
return mat;
}
return '<span class="tooltip" data-tooltip="' + tooltips[mat].replace(/"/g, '"') + '">' + mat + '</span>';
});
return str;
}
http://jsfiddle.net/rooseve/6wRUF/
Many of the other answers are just band-aids on the core problem: sometimes the searches are found in the replacements of other strings.
For examples some of the other naive solutions that will break easily:
But if we think back to the core problem there is an obvious solution:
Don't let the replacements be replaceable.
So, do two passes instead. Steps:
{ search: "Size", tooltip: "Long description" }
.<span class='tooltip' data-tooltip='The size is one thing, the color is another.'>Size and color</span>
.This way the whole operation acts as one search/replace and does not have the possibility of replacing matches in other replacements.
I'd use the following:
//usual regex
var re = new RegExp("("+key+")(?!(?:<|.+?'>))", "g");
//regex to be applied on reversed string, same concept as regex above
var reRev = new RegExp("("+key.reverse()+")(?!(?:.+'=|>))", "g");
//start of the span tag including the tooltip
var repl = "<span class='tooltip' data-tooltip='"+value+"'>";
//end of the span tag
var replEnd = "</span>";
//first replacement
output = output.replace(re, repl+"$1"+replEnd);
//now reverse the whole string and do a second replacement,
//this, together with the reversed regex (which uses a lookahead) mimics a lookbehind (which is unfortunately not available in JS)
output = output.reverse().replace(reRev, replEnd.reverse()+"$1"+repl.reverse()).reverse();
Regex-Demo @ regex101
JS-Demo @ JSFiddle
See the JSFiddle as for the replacement of sentences you have to order the input-array first!
For every replacement the regex matches we replace it with the according span-tooltip-construct.
As JS has no lookbehind we have to mimic it to replace every occurence of the keywords, because the first regex will fail to match keywords before an opening <span>
-tag, which could easily be solved with a lookbehind:
To mimic a lookbehind we use a lookahead, but simply reverse the whole text beforehand. The only downside is, that you have to reverse your regex, too... manually! For bigger expressions this will be a pain, but in this case it's fairly easy. If you reverse your input you don't want to match keywords if there comes a .+'=
or a >
afterwards.
For the regex itself:
it matches only the keyword if it is not followed by a <
(which would mark the </span>
tag) and if it is not followed by .+'>
, which means several chars and a '>
which would mark the end of a data-tooltip
attribute.
I made it case sensitive. If you want it to be case insensitive use the gi
flags instead of only the g
flag.
This way you're not limited to single words, you may replace "I am a sentence" with a tooltip of your choice, too. Same concept applies to the reversed regex.
You may need to adjust the replacement according to your datastructure. If it comes from user input, then an associative array may be right for you:
var replacements = new Array ();
replacements['Size'] = "The Size of a Unit is controlled by the Color of the Unit.";
replacements['Color'] = "The Color of a Unit is a standard Setting.";
It could be this simple if there were no multi-word replacements.
var tooltips = {
"Size":"The Size of a Unit is controlled by the Color",
"Color": "bar",
"Time and Size": "foo"
}
var text = "Here we have something of Size 20 and Color red. it's very important that the Time and Size of the work and kept in sync."
var replaced = text.split(" ").map(function(token) {
return tooltips[token] || token;
}).join(" ");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With