I am building a map visualization with d3.js. I am drawing filled polygons for both the US states and counties. The SVG layer for counties is under the layer for states. The sta
Here's a d3 function which does what you want.
It passes events to lower layers by temporarily disabling pointer events on the top layer, and manually triggering the mouse event on the next layer.
function passThruEvents(g) {
g .on('mousemove.passThru', passThru)
.on('mousedown.passThru', passThru)
;
function passThru(d) {
var e = d3.event;
var prev = this.style.pointerEvents;
this.style.pointerEvents = 'none';
var el = document.elementFromPoint(d3.event.x, d3.event.y);
var e2 = document.createEvent('MouseEvent');
e2.initMouseEvent(e.type,e.bubbles,e.cancelable,e.view, e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget);
el.dispatchEvent(e2);
this.style.pointerEvents = prev;
}
}
If you want to do something when you click a state, like changing the border style, or the opacity of the shape, you can use the fact that every county belongs to a state and just group the counties in states, capturing the 'click' event in the county, select the corresponding state element and change its visual attributes. A mockup of this strategy:
// Setup the visualization, assuming that you have a div with id 'chart'
var width = 300,
height = 300,
div = d3.select('#chart'),
svg = div.append('svg')
.attr('width', width)
.attr('height', height);
// Array of states, each one containing one or more counties. In this example,
// the states are rectangles, but you get the idea
var states = [
{
width: 300,
height: 300,
name: 'Fake State',
counties: [
{x: 5, y: 5, width: 95, height: 290, fill: '#b4a0a0'},
{x: 100, y: 5, width: 150, height: 290, fill: '#b4c0a0'},
{x: 250, y: 5, width: 45, height: 290, fill: '#a4a0c0'}
]
}];
// Create a group for each state, with class state
var gstates = svg.selectAll('g.state')
.data(states)
.enter()
.append('g')
.classed('state', true);
// Add the state background, in this case, a transparent rectangle
gstates
.append('rect')
.classed('state', true)
.attr('width', function(d) { return d.width; })
.attr('height', function(d) { return d.height; })
.attr('fill', '#444')
.attr('fill-opacity', 0.0);
// For each group, add rectangles for each county, binding them to the
// county array of each state.
gstates.selectAll('rect.county')
.data(function(d) { return d.counties; })
.enter()
.append('rect')
.classed('county', true)
.attr('x', function(d) { return d.x; })
.attr('y', function(d) { return d.y; })
.attr('width', function(d) { return d.width; })
.attr('height', function(d) { return d.height; })
.attr('fill', function(d) { return d.fill; })
.on('mouseover', function() {
d3.select(this).attr('fill-opacity', 0.5);
})
.on('mouseout', function() {
d3.select(this).attr('fill-opacity', 0.9);
})
.on('click', function() {
// Retrive the parent group of each county, and then select
// the shape corresponding to the state and alter its properties
// you can also access the state data
var parent = d3.select(d3.select(this).node().parentNode),
state = parent.select('rect.state');
// Output 'Fake State'
console.log(parent[0][0].__data__.name);
state.attr('fill-opacity', 1.0);
});
There is a working fiddle here: http://jsfiddle.net/pnavarrc/PGTCM/5/. Regards,