I'm trying to get a simple mouseover to work on some rectangles in d3. If I append the SVG to "body" everything works just fine (first line of code). If I change the select to ".chart1" instead of "body" the mouseovers don't work. Has anyone seen this before?
var chart = d3.select(".chart1").append("svg")
.attr("width", 250)
.attr("height", 50);
data = ["a", "b", "c",]
for(var i = 0; i < data.length; i++){
var rect = chart.append("rect")
.attr("x", 20*i)
.attr("y", 10)
.attr("width", 15)
.attr("height", 15)
chart.selectAll('rect')
.on("mouseover", function() {
d3.select(this)
.attr("opacity", .5)
})
.on("mouseout", function() {
d3.select(this)
.attr("opacity", 1)
});
jsfiddle: https://jsfiddle.net/jasonleehodges/um5f5ysv/
The problem is not because you are appending svg to body or div with class .chart1
The problem of mouseover not working is in here.
var chart = d3.select(".chart1").append("svg")
.attr("width", 250)
.attr("height", 50)
//.style("pointer-events", "none");//WHY DISABLE MOUSE EVENT ON SVG
Fix would be to remove .style("pointer-events", "none");
and it will work on all the cases without any anomaly.
Working code here
On another note you should not use a for loop, well that's not the d3 way(as put by #Gerardo Furtado).
so your code with for:
for(var i = 0; i < data.length; i++){
var rect = chart.append("rect")
.attr("x", 20*i)
.attr("y", 10)
.attr("width", 15)
.attr("height", 15)
instead should be
var rect = chart.selectAll("rect").data(data).enter().append("rect")
.attr("x", function(d,i){return 20*i})
.attr("y", 10)
.attr("width", 15)
.attr("height", 15)
.attr("fill", "#cc004c")
.attr("title","NBC")
.attr("data-toggle","tooltip")
working code here
Related
I could locate rectangle using the following.
I want to replace these rectangles with image/icon(jpg/url)
for(var i=0; i< tempdata.length; i++)
{
var name = "rect"+i;
d3.select("#floor svg").selectAll('rect')
.data(tempdata).enter()
.append("svg:rect")
.attr("stroke", "black")
.attr("fill", (d,i)=>tempdata[i].SENSOR_COLOR)
//.attr("r", 5)
.attr("width", 20)
.attr("height", 20)
.attr("x", (d,i)=>tempdata[i].CX)
.attr("y", (d,i)=>tempdata[i].CY)
.attr("idCircle",(d,i)=>name)
}
First of all: don't use for loops to retrieve data in D3. D3 already provides all you need to bind and retrieve your data. Indeed, there are several situations where it's a good idea to use a for loop, but this is not one of them.
Regarding your question: instead of append("rect"), you have to use append("image"):
var images = svg.selectAll(".images")
.data(data)
.enter()
.append("image");
In this demo snippet, I set the url of the images in the data array, using a key conveniently named url. Then, you have to append them using:
.attr("xlink:href", function(d){return d.url})
Here is the demo snippet, using favicons:
var svg = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", 200);
var data = [{url:"https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2009/02/amazon.gif", x:20, y:40},
{url:"https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2009/02/skype.gif", x:90, y:110},
{url: "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2009/02/espn.gif", x:150, y:150},
{url: "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2009/02/twitter.gif", x:180, y:50}];
var images = svg.selectAll(".images")
.data(data)
.enter()
.append("image");
images.attr("xlink:href", function(d){return d.url})
.attr("x", function(d){return d.x})
.attr("y", function(d){return d.y})
.attr("width", 16)
.attr("height", 16);
<script src="https://d3js.org/d3.v4.min.js"></script>
I am new to D3.js and am trying to build rectangles that represent all nodes from an XML file. So far so good but I want interactivity with each of the rectangles I draw and to be able to capture the nodes that have been touched for further processing. So let's say I click on a rectangle, I can make it react by doing an onclick event (like increasing the font size) but I can't seem to retrieve some of the info. I'd like to create an array with the text of each item that was clicked on.
Here's the code for one instance of the rectangle.
d3.select("#chart")
.append("svg")
.attr("width", 600)
.attr("height", 2000)
.style("background", "#93A1A1")
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 25)
.attr("height", 20)
.attr("width", 200)
.attr("title", "resourceDef")
.style("fill", "#CB4B19")
d3.select("svg")
.append("text")
.attr("x", 55)
.attr("y", 37)
.attr("font-size", 11)
.attr("font-family", "sans-serif")
.text("resourceDef")
.on("mouseover", function(d) {
tempText = this.text;
alert(tempText);
d3.select(this)
.attr("font-size", 15)})
.on("mouseout", function(d) {
d3.select(this)
.attr("font-size", 11)})
I can grab style info by using but not the title and I can't find that info anywhere. Thanks for your help, I know it's a long question with probably a simple answer.
You can attach a mouse over event on the rectangle DOM by doing something like this:
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 25)
.attr("height", 20)
.attr("width", 200)
.attr("title", "resourceDef")
.style("fill", "#CB4B19")
.on("click", function (d) {
var t = d3.select(this).attr("title");
//pushing the title into the array.
clickedTitles.push(t);
console.log(t);
});
You can get the attribute of a DOM(in your case tite) by doing something like this:
.on("click", function (d) {
var t = d3.select(this).attr("title");
clickedTitles.push(t);
console.log(t)
})
You can store the clicked rectangles title in an array like this:
//create an array
var clickedTitles = [];
//in your click function push the title into the array
clickedTitles.push(t);
//use the clickedTitles where ever you need in the code
Full code is here.
I am not sure what's going on, but I have 2 very simple examples set up to show what I am asking.
Both examples have a 'g' that contains a 'rect' and 'text'.
In the 1st example, I am setting up drag on the 'g' itself, i.e., if you mousedown anywhere in that group and drag, it will drag the entire thing (both 'rect' and 'text') around the viewpoint.
http://jsfiddle.net/wup4d0nx/
var chart = d3.select("body").append("svg")
.attr("height", 500)
.attr("width", 500)
.style("background", "lightgrey");
var group = chart.selectAll("g")
.data(["Hello"])
.enter()
.append("g")
.attr("id", function (d) { return d;});
var rect = group.append("rect")
.attr("stroke", "red")
.attr("fill", "blue")
.attr("width", 200)
.attr("height", 200)
.attr("x", 10)
.attr("y", 10);
var label = group.append("text")
.attr("x", 40)
.attr("y", 40)
.attr("font-size", "22px")
.attr("text-anchor", "start")
.text(function (d) { return d;});
// Set up dragging for the entire group
var dragMove = function (d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
};
var drag = d3.behavior.drag()
.origin(function (data) {
var element = d3.select("#" + data);
return {
x: d3.transform(element.attr("transform")).translate[0],
y: d3.transform(element.attr("transform")).translate[1]
};
})
.on("drag", dragMove);
group.call(drag);
In the 2nd example, which doesn't work and is what I am interested in, I want ONLY THE TEXT to be something the user can grab to drag the entire group around.
I tried many attempts. Some don't work at all, some work but flicker like the example I provide here:
http://jsfiddle.net/9xeo7ehf/
var chart = d3.select("body").append("svg")
.attr("height", 500)
.attr("width", 500)
.style("background", "lightgrey");
var group = chart.selectAll("g")
.data(["Hello"])
.enter()
.append("g")
.attr("id", function (d) { return d;});
var rect = group.append("rect")
.attr("stroke", "red")
.attr("fill", "blue")
.attr("width", 200)
.attr("height", 200)
.attr("x", 10)
.attr("y", 10);
var label = group.append("text")
.attr("x", 40)
.attr("y", 40)
.attr("font-size", "22px")
.attr("text-anchor", "start")
.text(function (d) { return d;});
// Set up dragging for the entire group USING THE LABEL ONLY TO DRAG
var dragMove = function (d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this.parentNode).attr("transform", "translate(" + x + "," + y + ")");
};
var drag = d3.behavior.drag()
.origin(function (data) {
var element = d3.select("#" + data);
return {
x: d3.transform(element.attr("transform")).translate[0],
y: d3.transform(element.attr("transform")).translate[1]
};
})
.on("drag", dragMove);
label.call(drag);
What's going on with this that it flickers and what am I doing wrong?
Any help would be greatly appreciated!
Thanks!
I'm not sure exactly why it is flickering (as I am not too familiar with D3), but one way to get it to stop is to use the source event for D3:
// 50 is the offset x/y position you set for your text
var x = d3.event.sourceEvent.pageX - 50;
var y = d3.event.sourceEvent.pageY - 50;
Edit: While the above code works, it causes the box to initially "jump" to the coordinates of the text, A better fix would be to take your first example and just filter our events that aren't executed on the text element. Try putting the following at the top of the dragMove method:
if(d3.event.sourceEvent.target.nodeName !== 'text') {
return;
}
Try d3.event.sourceEvent.stopPropagation(); inside on-drag function
I'm trying to create a circular avatar with D3.js but I cannot get my image to show up in my circle. I'm using a svg pattern def to attempt to fill the circle with an image.
Can anyone tell me what I'm doing wrong below? Thank you.
var config = {
"avatar_size" : 48
}
var body = d3.select("body");
var svg = body.append("svg")
.attr("width", 500)
.attr("height", 500);
var defs = svg.append('svg:defs');
defs.append("svg:pattern")
.attr("id", "grump_avatar")
.attr("width", config.avatar_size)
.attr("height", config.avatar_size)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", 'images/avatars/avatar_grumpy.png')
.attr("width", config.avatar_size)
.attr("height", config.avatar_size)
.attr("x", 0)
.attr("y", 0);
var circle = svg.append("circle")
.attr("cx", config.avatar_size)
.attr("cy", config.avatar_size)
.attr("r", config.avatar_size)
.style("fill", "#fff")
.style("fill", "#grump_avatar");
"Fill" is a style property, you have to use CSS url() notation for the reference to the pattern element.
Once you fix that, you'll discover that you also have your sizes wrong -- unless your intention was to have four copies of the avatar tiled in the circle!
P.S. I would normally have left this just as a comment, and marked this for closure as a simple typo, but I wanted to try out Stack Snippets:
var config = {
"avatar_size" : 48
}
var body = d3.select("body");
var svg = body.append("svg")
.attr("width", 500)
.attr("height", 500);
var defs = svg.append('svg:defs');
defs.append("svg:pattern")
.attr("id", "grump_avatar")
.attr("width", config.avatar_size)
.attr("height", config.avatar_size)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", 'http://placekitten.com/g/48/48')
.attr("width", config.avatar_size)
.attr("height", config.avatar_size)
.attr("x", 0)
.attr("y", 0);
var circle = svg.append("circle")
.attr("cx", config.avatar_size/2)
.attr("cy", config.avatar_size/2)
.attr("r", config.avatar_size/2)
.style("fill", "#fff")
.style("fill", "url(#grump_avatar)");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I'm pretty new to d3 and have been following this tutorial: http://christopheviau.com/d3_tutorial/
I'm stuck on the 'Binding Data' example - it's pretty simple but the code just won't produce anything. I've poked around here and haven't found the question listed so I thought I'd ask away.
Here's the code:
var dataset = [],
i = 0;
for(i = 0; i < 5; i++) {
dataset.push(Math.round(Math.random() * 100));
}
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 75);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("height", 40)
.attr("width", 75)
.attr("x", function (d, i) {
return i * 80
})
.attr("y", 20);
Other examples on the site work fine.
Thanks in advance - any ideas would be appreciated.
Unfortunately the code listed in the tutorial is incorrect. The svg element "circle" is specified by three attributes, "cx", x-axis coordinate of the center of the circle, "cy", y-axis coordinate of the center of the circle, and "r", the radius of the circle. I got this information from the w3 specification for an SVG circle.
I would recommend inspecting the JavaScript in the tutorial page to help iron out any other inconsistencies. Here it is:
<script type="text/javascript">
var dataset = [],
i = 0;
for(i=0; i<5; i++){
dataset.push(Math.round(Math.random()*100));
}
var sampleSVG = d3.select("#viz5")
.append("svg")
.attr("width", 400)
.attr("height", 100);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", function(d, i){return i*80+40})
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");})
.on("mousedown", animateFirstStep);
function animateFirstStep(){
d3.select(this)
.transition()
.delay(0)
.duration(1000)
.attr("r", 10)
.each("end", animateSecondStep);
};
function animateSecondStep(){
d3.select(this)
.transition()
.duration(1000)
.attr("r", 40);
};
</script>
I also created a JSFiddle which you can utilize to get the basic idea that the author of the tutorial is trying to convey, with respect to utilizing d3.js data, here.
svg circles use cx, cy, and r - not x, y, height, and width. I've correct the example code below:
var dataset = [];
for(var i = 0; i < 5; i++) {
dataset.push(Math.round(Math.random() * 100));
}
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 400)
.attr("height", 400);
sampleSVG.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "black")
.attr("r", 10)
.attr("cx", function (d, i) {
return i * 80 + 10;
})
.attr("cy", function (d, i) {
return d;
});
http://jsfiddle.net/q3P4v/7/
MDN on svg circles: https://developer.mozilla.org/en-US/docs/SVG/Element/circle