In my project I am trying to combine 'toggling' a Tipsy pop-up in a D3 force layout. Basically I want to be able to click the images, show a popup there and allow the user some interaction. If the user is done, he either clicks another image or closes it by clicking the same image.
The D3 part of the code does some rendering of images in a force layout. Basically the code is like this:
var node = vis.selectAll("circle.node")
.data(data.nodes)
.enter().append("svg:image")
.attr("class", "node")
.attr("xlink:href", function (d) { return d.icon; })
.attr("x", function (d) { return d.x - 12; })
.attr("y", function (d) { return d.y - 12; })
.attr("width", '24px')
.attr("height", '24px')
.style("fill", function (d) { return fill(d.group); })
The link for Tipsy can be found here: http://onehackoranother.com/projects/jquery/tipsy/# . To get my basics straight, I've tested it like this:
$('.node').tipsy({
gravity: 'w',
opacity: 1,
html: true,
title: function () {
var d = this.__data__, icon = d.icon;
return '<img src="' + icon + '" style="width: 100px; height:100px;"/>;
}
});
So far so good; using this code, if you hover, it will all work.
Last adjustment, I've added trigger: 'manual'
to the options of Tipsy and added some code to the D3 node:
// this variable is declared on top somewhere:
var prev = null;
// and to the D3 code, this is added:
.on("click",
function () {
if (this == prev)
{
$(this).tipsy("hide");
prev = null;
}
else
{
// show
if (prev != null) {
$(prev).tipsy("hide");
}
$(this).tipsy("show");
prev = this;
}
});
As you can see, the code closely reflects the code on http://onehackoranother.com/projects/jquery/tipsy/# (section: Manually triggering a tooltip).
Now, if we run this code, it suddenly doesn't work anymore. On closer inspection, I've noticed that in the Tipsy code, the problem is in this piece:
} else if (typeof options == 'string') {
console.debug(this); // added some debug info
var tipsy = this.data('tipsy');
if (tipsy) tipsy[options]();
return this;
}
While running this code I can see that this.data('tipsy')
is always undefined
. However, the elements that are selected, seem to be correct.
I've looked through other SO posts, so far without luck. Suggested by some, I've experimented with different versions of jQuery, tried to bind onclick from jQuery instead of D3, etc. So far, all with the same behavior.
Can someone please tell me what I'm missing ?
Minimal test case
A simple minimal test-case of Tipsy & D3 that this work is based on can be found on this link: http://bl.ocks.org/ilyabo/1373263 . The html code can be replaced with this:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="jquery.tipsy.js"></script>
<link href="tipsy.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var w = 800, h = 500;
var colors = d3.scale.category20();
var vis = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
var data = d3.range(20).map(function(i) {
return { i:i, x:Math.random()*w, y:Math.random()*h, r:Math.random()*30 }
});
var prev = null;
vis.selectAll("circle")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "black")
.attr("fill", function(d, i) { return colors(i); })
.attr("cx", function(d, i) { return d.x; })
.attr("cy", function(d, i) { return d.y; })
.attr("r", function(d, i) { return d.r; })
.on("click",
function () {
if (this == prev)
{
$(this).tipsy("hide");
prev = null;
}
else
{
if (prev != null)
{
$(prev).tipsy("hide");
}
$(this).tipsy("show");
prev = this;
}
});
$('svg circle').tipsy({
trigger: 'manual',
gravity: 'w',
html: true,
title: function() {
var d = this.__data__, c = colors(d.i);
return 'Hi there! My color is <span style="color:' + c + '">' + c + '</span>';
}
});
</script>
</body>
</html>
The problem is not with d3.
The problem is with tipsy.
Here I am using the same tipsy js up in the git
And it works perfectly fine.
So the issue is that you may be using some old buggy version of tipsy.
//setting the tipsy to all circles in svg
$('svg circle').tipsy({
trigger: 'manual',
gravity: 's',
html: true,
title: function() {
var d = d3.select(this).data(),
c = colors(d.i);
return 'Hi there! My color is <span style="color:' + c + '">' + c + '</span>';
}
});
SVG circle click event show hide tipsy manually
.on("click",
function() {
if (this == prev) {
$(this).tipsy("hide");
prev = null;
} else {
if (prev != null) {
$(prev).tipsy("hide");
}
$(this).tipsy("show");
prev = this;
}
});
Wrking code here
Hope this helps!