d3 handling mouse events in charts - javascript

I am trying to make an interactive pie chart that reacts to mouse clicks. At the moment I made it possible for a tooltip to come up once a pie slice is clicked on. But how can I make it disappear if the user clicks again on the same slice?
.on("click", function(d) {
tooltip.transition()
.duration(450)
.style("opacity", 0.7);
tooltip.html("<b>" + d.name + ": " + d.value + "</b>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY-20) + "px");
});

If the data in your selection are objects, then you can store within each datum whether it's selected or not. For example,
.on("click", function(d, i) {
if (!d.selected){
tooltip.transition()
.duration(350)
.style("opacity", 0.9);
tooltip.html("<b>" + stats[i].status + ": " + d.value + "</b>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY-margin*0.8) + "px");
d.selected = true;
// deselect all other arcs
arcs.each(function(d, j){
if (i != j) d.selected = false;
});
}
else {
tooltip.transition()
.duration(200)
.style("opacity", 0);
d.selected = false;
}
});
Note that this ensures that all other arcs are deselected when you select a new arc.

Related

d3 onerror default image with html tooltip tag

I am making a tooltip with images and other data for a mouseover event in my D3 project. I would like to use a default image when my data images are missing. I am not able to get onerror to work with the html string I have written and I would be grateful if someone could take a look at it for me. I think my trouble iw with formatting of this string.
This is the relevant bit of code:
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(50)
div.transition()
.duration(200)
.style("opacity", .9);
div.html('<img src="images/'+ d.properties.objectNumber +'.jpg" onError="this.onerror=null;this.src="images/00037631.jpg" style ="width:4em;" /> ' + d.properties.name)
//div.html('<img src="images/'+ d.properties.objectNumber +'.jpg" onError="this.src="images/ANMS0533[020].jpg" style ="width:4em;" /> ' + d.properties.name)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
My image shows up fine where it exists, but if the image is missing I get a no image icon and I would like to see a default image, or nothing.
I hope someone can help.
I would probably solve this problem like this, either use id or class on the uploaded img tag then select it using d3 and append on on error event to it.
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(50)
div.transition()
.duration(200)
.style("opacity", .9);
div.html('<img id="loadingPic" src="images/'+ d.properties.objectNumber +'.jpg"/> ' + d.properties.name)
d3.select("#loadingPic").on("error", function(){
let el = d3.select(this);
el.attr("src", "images/00037631.jpg").style("width", "4em");
el.on("error", null);
})
//div.html('<img src="images/'+ d.properties.objectNumber +'.jpg" onError="this.src="images/ANMS0533[020].jpg" style ="width:4em;" /> ' + d.properties.name)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})

D3 js selectAll each doesn't iterate

I am trying to implement the FishEye lens (Cartesian) in my scatterplot.
I am trying to follow this approach, but apparently my selector already fails.
I have my FishEye defined as
var fisheye = d3.fisheye.circular().radius(120);
svg.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
console.log("here " + points.selectAll("circle").length);
points.selectAll("circle").each(function(d) {
console.log("aaa");
d.fisheye = fisheye(d);
/*points.selectAll("circle")
.each(function(d) {
console.log("???");
this.attr("cx", function(d) { return d.fisheye.x; })
this.attr("cy", function(d) { return d.fisheye.y; })
this.attr("r", function(d) { console.log("hype"); return 10; });
}); */
});
});
and my points is defined as
points = svg.append("g")
.attr("class", "point")
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { // Set the x position using the x-scale
return x(d.x);
})
.attr("cy", function(d) { // Set the y position using the y-scale
return y(d.y);
})
.attr("r", 5) // Set the radius of every point to 5
.on("mouseover", function(d) { // On mouse over show and set the tooltip
if(!isBrushing){
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(d.symbol + "<br/> (" + parseFloat(x(d.x)).toFixed(4)
+ ", " + parseFloat(y(d.y)).toFixed(4) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}
})
.on("mouseout", function(d) { // on mouseout, hide the tooltip.
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
The console.log with "here" is spamming when I am moving the mouse, and shows the correct amount. Hwoever, the each loop is never executed as I do not see "aaa". I have also tried to just use selectAll("circle") but that doesn't work either.
What am I doing wrong and how can I get my FishEye to work?

Tooltip is not displaying in the exact position in D3.js code

Tooltip is not diaplying in the appropriate area.I'm using D3.js
var divLink = d3.select(el[0]).append("div")
.attr("class", "tooltip")
.style("opacity", 0);
.attr("id",function(d){
return d.linkID;})
.style("stroke-width", 0.7)
.style("fill", "none")
.on("mouseover", function(d) {
var dx = (d.x+30), dy = (d.y+155)-$('#svgWrapper').scrollTop();
divLink.transition()
.duration(200)
.style("opacity", .9);
// When links are hovered, tool tip is created
if(d.source.type.toLowerCase()=="rack" || d.target.type.toLowerCase()=="rack")
{
divLink.html("<b>Link Details: </b><br/><br/>"+"<b>Link Id: </b>"+d.linkID +"<br/>"
+"<b>Device1: </b>"+d.source.name+"<br/>"+ "<b>Device2: </b>"+d.target.name+"<br/>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}else{
divLink.html("<b>Link Details: </b><br/><br/>"+"<b>Link Id: </b>"+d.linkID +"<br/>"+"<b>Link Speed: </b>"+d.linkSpeed+"<br/>"
+"<b>Device1: </b>"+d.source.name+"<br/>"+ "<b>Ports at Device1: </b>"+d.sport+"<br/>"+"<b>Device2: </b>"+d.target.name+"<br/>"
+ "<b>Ports at Device2: </b>"+d.tport+"<br/>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}
})
.on("mouseout", function(d) {
divLink.transition()
.duration(500)
.style("opacity", 0);
});
Something is in the style, because of that i'm getting this error.
Actually i'm having one more tooltip for another tool there the tooltip is placing in the exact area.
var div = d3.select(el[0]).append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Device Node Creation
var node = svg.selectAll(".node")
.data(networkObject.nodes)
.enter()
.append("g")
.attr("id",function(d){return d.name;})
.attr("class","node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y) + ")"; })
.on("mouseover", function(d) {
var dx = (d.x+30), dy = (d.y+155)-$('#svgWrapper').scrollTop();
div.transition()
.duration(200)
.style("opacity", .9);
// Tool tip when hovered on particular device node
if(d.type.toLowerCase()=="rack"){
div.html("<b>Rack Details: </b><br/><br/>"+"<b>Rack Id: </b>"+d.name +"<br/>"+"<b>TOR Switches: </b>"+d.tor+"<br/>"+"<b>Management Switches: </b>"+d.mgmt+"<br/>"+"<b>Hosts: </b>"+d.host+"<br/>"+"<b>Status: </b>"+d.errorst+"<br/>")
.style("left",dx + "px")
.style("top", dy + "px");
}else if(d.type.toLowerCase()=="switch" && d.role.toLowerCase()=="spine"){
div.html("<b>Switch Details: </b><br/><br/>"+"<b>Switch Id: </b>"+d.name +"<br/>"+ "<b>Role: </b>"+d.role+"<br/>"+"<b>IP Address: </b>"+d.ip+"<br/>"+"<b>Status: </b>"+d.errorst+"<br/>")
.style("left", dx + "px")
.style("top", dy + "px");
}else if(d.type.toLowerCase()=="switch"){
div.html("<b>Switch Details: </b><br/><br/>"+"<b>Switch Id: </b>"+d.name +"<br/>"+ "<b>Role: </b>"+d.role+"<br/>"+"<b>Rack: </b>"+d.rack+"<br/>"+"<b>IP Address: </b>"+d.ip+"<br/>"+"<b>Status: </b>"+d.errorst+"<br/>")
.style("left", dx + "px")
.style("top", dy + "px");
}else if(d.type.toLowerCase()=="host"){
div.html("<b>Host Details: </b><br/><br/>"+"<b>Host Id: </b>"+d.name +"<br/>"+"<b>Rack: </b>"+d.rack+"<br/>"+"<b>IP Address: </b>"+d.ip+"<br/>"+"<b>Status: </b>"+d.errorst+"<br/>")
.style("left", dx + "px")
.style("top", dy + "px");
}else if(d.type.toLowerCase()=="corporate"){
div.html("<b>Corporate Network</b><br/><br/>")
.style("left", dx + "px")
.style("top", dy + "px");
}
})
.on("mouseout", function(d) {
div.transition()
.duration(100)
.style("opacity", 0);
});
So actually i found the answer for this, i added the coordinates for the x and y. then the tooltip was working, I don't think, this should be a permanent solution for this.If anyone has any other answers please post that.Thanks
.style("left", (d3.event.pageX-460) + "px")
.style("top", (d3.event.pageY - 50) + "px");

All elements take the same values

I'm trying to assign different elements different attribtsty. But everyone is given the same (last) attribute. What's the matter?
for (var i = 1; i < 12; i++) {
d3.select("#id_" + i)
.text(parseFloat(data[i - 1] / 1000000).toFixed(2))
.on("mouseover", function (d) {
d3.select("#tooltip")
.style("left", "200px")
.style("top", d3.event.pageY - 30 + "px")
.select("#info")
.html("<b>" + keys[i - 2] + "</b>");
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function () {
d3.select("#tooltip").classed("hidden", true);
});
}
Link: JSFIDDLE
Try this:
for (var i = 1; i < 12; i++) {
(function (i) {
d3.select("#id_" + i)
.text(parseFloat(data[i - 1] / 1000000).toFixed(2))
.on("mouseover", function (d) {
d3.select("#tooltip")
.style("left", "200px")
.style("top", d3.event.pageY - 30 + "px")
.select("#info")
.html("<b>" + keys[i - 2] + "</b>");
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function () {
d3.select("#tooltip").classed("hidden", true);
});
})(i)
}
The code that you have written is an example of imperative programming, where you tell the computer how to do something.
D3 is an example of declarative programming, where you tell the computer what to do, and let the computer decide how to do it.
D3 was not designed to loop through to assign values like in your question. Instead of loops and conditionals and other "hows", you should focus on what you want to happen.
To do this, you should use what is called "data binding", where you bind your dataset to the svg to draw your text (here is a beginner tutorial/simple explanation http://bost.ocks.org/mike/circles/)
In relation to your problem, you should put your data in one object like this:
var data = [
["key": 1, "value": 0.0, "x": 84, "y": 310],
...
];
You then bind this data to the svg element, where you can then tell D3 to draw your text with those attributes without specifiying the exact implementation.
svg.selectAll("text")
.data(data)
.enter()
.attr("x", function(d, i) {
return d.x;
})
.attr("y", function(d, i) {
return d.y;
})
...
.on("mouseover", function(d, i) {
d3.select("#tooltip")
.style("left", "200px")
.style("top", d3.event.pageY - 30 + "px")
.select("#info")
.html("<b>" + d.key + "</b>");
d3.select("#tooltip").classed("hidden", false);
})
...
The function function(d, i) gives the actual object d as an argument, and i as the object's location in the array.
Your issue with the tooltips is that i is the same value (12) no mater which element is moused over. To see this, you can log i in your mouseover handler. There are a few ways you could recover the index of the element that is moused over. One of the easiest (though perhaps not cleanest) would be to recover it from the element ID.
for (var i = 1; i < 12; i++) {
d3.select("#id_" + i)
.text(parseFloat(data[i - 1] / 1000000).toFixed(2))
.on("mouseover", function (d) {
var index = this.id.slice(3,9); //ADDED THIS LINE
d3.select("#tooltip")
.style("left", "200px")
.style("top", d3.event.pageY - 30 + "px")
.select("#info")
.html("<b>" + keys[index - 2] + "</b>"); //USE THE INDEX
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function () {
d3.select("#tooltip").classed("hidden", true);
});
}
I also suspect that you mean keys[index-1] not keys[index-2]. The latter goes out of bounds of the array.

d3 link static tooltip to points

I am trying to create a scatter plot and want to show tooltips by clicking on each point. The tooltip will disappear only when the point is deselected (clicked again). Currently, selected points will have a black border with r=8. Deselected points have no visible black border with r=4.5.
With the code below, when I deselect the points, the tooltip won't go away. How can I link the tooltip to each point? Thanks!
.on("click", function (d) {
var clickTooltip = d3.select("#data_visualization").append("div").attr("class", "click_tooltip");
if (d3.select(this).attr("r") < 8) {
d3.select(this)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-opacity", 1)
.attr("r", 8);
clickTooltip.style("opacity", 0.62);
var clickTooltipText = "display";
clickTooltip.html(clickTooltipText)
.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 40) + "px");
} else {
d3.select(this)
.attr("r", 4.5)
.style("stroke-opacity", 0);
clickTooltip.style("opacity", 0);
}
}
You are appending a new element every time the click handler is called. Instead, create the element once and then select it:
var clickTooltip = d3.select("#data_visualization").append("div").attr("class", "click_tooltip");
.on("click", function (d) {
if (d3.select(this).attr("r") < 8) {
// etc
I figured it out. I am posting my answer here in case anyone is interested. The idea is to add an ID to each tooltip div. Later we can use JQuery to remove by ID.
.on("click", function (d) {
var pointID = "point_" + d3.select(this).attr("cx").replace(".", "_") + "_" + d3.select(this).attr("cy").replace(".", "_");
var clickTooltip = d3.select("#data_visualization")
.append("div")
.attr("id", pointID)
.attr("class", "click_tooltip");
if (d3.select(this).attr("r") < 8) {
d3.select(this)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-opacity", 1)
.attr("r", 8);
clickTooltip.style("opacity", 0.62);
var clickTooltipText = "display";
clickTooltip.html(clickTooltipText)
.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 40) + "px");
} else {
d3.select(this)
.attr("r", 4.5)
.style("stroke-opacity", 0);
d3.select("#" + pointID).remove();
}
}

Categories

Resources