I am unable to get the click to work on the circle. I have read various tips about how to attach clicks etc. to circles but I cannot get them to work so I must not understand them.
Any tips would be appreciated.
d3.json("d3_files/json/points-category-xyz.json",function(error,data){
svg.select("#points")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r",10)
.style("fill-opacity",.7)
.style("display",function(data){
return"block";
}) // end display style
.style("fill",function (data) {
if (data.category=="abc") {
return "blue"
} // endif
else {
return "red"
} // endelse
}) // end style fill
.style("stroke","#fff")
.on("click", function (){ // this doesn't appear to work
console.log("Hmmm");
alert("Hello");
}) // end click
.attr("transform",function(data){
return"translate("+projection([data.lng,data.lat])+")"
}) // end transform attr
.on("mouseout",function(d){
d3.select("body")
.select(".datamaps-hoverover")
.style("display","none");
d3.select(this)
.style("stroke-width","1px")
}) // end mouseout
.on("mouseover",function(d){
d3.select(this)
.style("stroke-width","4px")
.html(function() {
return node.attributes.name.value + "Popup here"
}) // end .html
}) // end mouseover
.on("mousemove",function(data){
var data=d3.mouse(this);
var n=this;
d3.select(this)
.style("stroke-width","4px")
d3.select("body")
.select(".datamaps-hoverover")
.style("display","block")
.style("top",data[1]+10+"px")
.html(function() {
return node.attributes.name.value + "Popup here"
}) // end html
.style("left",data[0]+"px")
}) // end mouseover
}); // end points json
The problem is that your circles are obscured by other elements and never receive pointer events. One solution to fix this is to append the circles last.
This worked for me.
link.on("click", function(d){
$scope.$apply(function() {
$scope.show(d);
});
d3.event.stopPropagation();
});
Related
I'm trying to develop a d3 navigation menu without using the normal li/ul approach. So far I have 2 levels and I'm using mouse events to trigger the changes. However, the first cycle works okay and the parent items go black on mouseout thereafter things start behaving oddly and this line doesn't execute; svg.selectAll(".lvl1").attr("fill", "black"); but the remove() process works. Have I missed something or is it hanging on an event? Any ideas that will help extend this approach to level 3 would also be appreciated. https://jsfiddle.net/sjp700/djcc6kxq/
lvl2.selectAll(".band")
.attr("width", function (d) { return d.width; })
.attr("height", 18)
.style("opacity", .5) // set the element opacity
.style("stroke", "black")
.attr("class", "tbd")
.style("cursor", "move")
.on('mouseover', over2)
.on('mouseout', out)
.attr("link", function (d) { return d.link; });
}
function out() {
var t = d3.select(this);
t.attr("fill", "pink")
setTimeout(function () {
svg.selectAll(".lvl2").remove();
svg.selectAll(".lvl1").attr("fill", "black");
}, 2000);
}
As mentioned in the comments, you need to style the rect not the g element.
Updated fiddle : https://jsfiddle.net/thatOneGuy/djcc6kxq/1/
Also, I have rearranged the colouring of the rects, so previously you had :
function out() {
var t = d3.select(this);
t.attr("fill", "pink")
//setTimeout(function() {
svg.selectAll(".lvl2").remove();
svg.selectAll(".lvl1 rect").attr("fill", "black");
// }, 2000);
}
But change it to this to keep the last selected tab coloured pink :
function out() {
//setTimeout(function() {
svg.selectAll(".lvl2").remove();
svg.selectAll(".lvl1 rect").attr("fill", "black");
var t = d3.select(this);
t.attr("fill", "pink")
// }, 2000);
}
To be honest, I wouldn't use the remove as when you try mouseover the level 2 elements, because you aren't over the parent anymore, they get removed. I would just create the structure and hide all at first. Then on mouseover of parent, show children, i.e set visibility to visible and on mouseout, set visibility to hidden. Just saves you removing and then recreating elements.
Using D3.js, I have something like this:
var sets = [
{ data:[{date:1980,value:10},{date:1981,value:20},{date:1982,value:30}] },
{ data:[{date:1981,value:10},{date:1982,value:20},{date:1983,value:30}] },
{ data:[{date:1982,value:10},{date:1983,value:20},{date:1984,value:30}] }
];
And I bind it to make a chart like this:
var paths = g.selectAll("path")
.data(sets);
paths.enter()
.append("path")
.datum(function(d) { return d.data; })
.attr("class","line")
.attr("d", line);
Where g is a g element inside an svg element. This works. For each item in set I get a path using the values in data. Now what I want to do is click an element and replace the data with a different set:
var altData = [
{ data:[{date:1980,value:30},{date:1981,value:20},{date:1982,value:10}] },
{ data:[{date:1981,value:10},{date:1982,value:20},{date:1983,value:30}] },
{ data:[{date:1982,value:10},{date:1983,value:20},{date:1984,value:0}] }
];
d3.select("#transition").on("click", function() {
paths.data(altData);
console.log("click");
});
But the paths.data(altData) doesn't appear to do anything. There are no console errors, but the chart doesn't change. What do I need to do to tell it that the data has changed and the lines should be redrawn? As a bonus, I'd really like this transition to be animated.
Full fiddle
Basically you need to tell d3 to redraw it. In your case, it is by calling attr("d", line).
For transition, put transition() between two attr("d", fnc). Your onclick function will look like the following
d3.select("#transition").on("click", function() {
paths.attr("d", line)
.transition()
.attr("d", function(d, i){
return line(altData[i].data)
})
});
Jsfiddle http://jsfiddle.net/8fLufc65/
Look at this plnkr that will change the data when transition is clicked.
I made the part that draws the lines into a function and pass the data for which it should be drawing the lines.
drawPaths(sets) ;
function drawPaths(sets) {
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var paths = g.selectAll("path")
.data(sets);
paths.enter()
.append("path")
.datum(function(d) { console.log(d); return d.data; })
.attr("class","line")
.attr("d", line);
}
I want to implement stack bar with toggle legend using D3.js ,on click on the legend, stack bar should get redrawn.If the legend was active,rectangle slab corresponding to the legend should get disappear and vise versa.
On click on the legend, I am not able to update the data binded with the group element and rect element present inside the group element properly.
In the DOM tree,on click on the legend,rect element is getting appended and added to first group element, rect element should actually get updated only.
You can view the source code in Jsfiddle here
I want something similar to stack bar with legend selection as implemented here in nvd3
function redraw() {
var legendselector = d3.selectAll("g.rect");
var legendData = legendselector.data();
var columnObj = legendData.filter(function(d, i) {
if (d.active == true)
return d;
});
var remapped = columnObj.map(function(cause) {
return dataArch.map(function(d, i) {
return {
x : d.timeStamp,
y : d[cause.errorType]
};
});
});
var stacked = d3.layout.stack()(remapped);
valgroup = stackBarGroup.selectAll("g.valgroup").data(stacked, function(d) {
return d;
}).attr("class", "valgroup");
valgroup.enter().append("svg:g").attr("class", "valgroup").style("fill",
function(d, i) {
return columnObj[i].color;
}).style("stroke", function(d, i) {
return d3.rgb(columnObj[i].color).darker();
});
valgroup.exit().remove();
rect = valgroup.selectAll("rectangle");
// Add a rect for each date.
rect = valgroup.selectAll("rectangle").data(function(d, i) {
return d;
}).enter().append('rect');
valgroup.exit().remove();
rect.attr("x", function(d) {
return x(d.x);
}).attr("y", function(d) {
return y(d.y0 + d.y);
}).attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
}).attr("width", 6);
}
function redraw() did not use transition inside it
You need to get more understanding about object constancy. (Three state described by the author)
I wrote an example of group chart in d3, the legend is interactable and works well, because i am new to d3, maybe the pattern or standard used is not very formal.
Listed it below only for you reference, hope it helps, good luck :-p
fiddle
I was able to incorporate the Pan/Zoom example fine. The problem is I also want to be able to drag various groups of elements in addition to zooming. What I'm doing now, is disabling the "pan" part of the zoom/drag behavior, and having a separate drag event listener on the groups I want the user to be able to drag. The problem now is that when I drag a group, then go to zoom in, the group jumps. I was wondering what the best way to go about fixing this problem might be? I was thinking about changing all of the individual elements positions' directly instead of just translating the whole group, but that seems really brittle and inefficient to me.
EDIT: Here is my code. I've commented the pertinent parts, but left the whole directive for context
window.angular.module('ngff.directives.board', [])
.directive('board', ['socket',
function(socket) {
var linker = function(scope, elem, attrs) {
var nodeDragging = false;
scope.svg = d3.select(elem[0]).append("svg")
.attr('width', elem.parent().width())
.attr('height', elem.parent().height())
.attr('class', 'boardBackground')
.on('mouseup', function(){
nodeDragging = false;
})
.append('g')
.call(d3.behavior.zoom().on('zoom', zoom))
.append('g')
scope.svg.append("rect")
.attr("class", "overlay")
.attr("width", elem.parent().width())
.attr("height", elem.parent().height());
scope.$on('addFormation', function(event, formation) {
var group = formation.select('g');
scope.svg.node().appendChild(group.node())
var pieces = group.selectAll('circle')
.on('mousedown', function(){
//Here I set a flag so I can check for node dragging when my zoom function is called
nodeDragging = true;
})
.call(d3.behavior.drag().on('drag', move));
})
function move() {
var dragTarget = d3.select(this);
dragTarget
.attr('cx', function() {
return d3.event.dx + parseInt(dragTarget.attr('cx'))
})
.attr('cy', function() {
return d3.event.dy + parseInt(dragTarget.attr('cy'))
})
}
//******I return here if the user is dragging the node to keep all of the elements from being translated
function zoom() {
if(nodeDragging)return
console.log('zoom')
scope.svg
.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
}
return {
restrict: 'E',
link: linker
}
}
])
I´m back with another d3.js problem.
I have an choropleth map and i want a tooltip which shows me the correct value of the community on an mouseover function.
It works nearly perfect, there is only one problem, there is no "update" of the value. on Every county it is the same.
I hope someone has an answer for me. Thank you.
Here is my code snippet:
var feature = group.selectAll("path")
.data(collection.features)
.enter()
.append("path")
//.transition()
//.duration(9000)
.attr("fill",function(d) {
//Get data value
var value = d.properties.EINWOHNER;
//console.log(value);
if (value) {
//If value exists…
return color(value);
}
else {
//If value is undefined…
return "none";
}
})
.on("mouseover", function(d) {
d3.select("#tooltip")
.data(collection.features)
.select("#value")
.text(function(d){
return d.properties.EINWOHNER;
});
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
To set the text, use
.text(d.properties.EINWOHNER);
You're currently getting the value from the data bound to the #value DOM element for each of them. You also don't need to pass in data there -- you already have the current data in d. So the complete code would look like this.
.on("mouseover", function(d) {
d3.select("#tooltip")
.text(d.properties.EINWOHNER);
d3.select("#tooltip").classed("hidden", false);
})