Situation:
I am working on a google map with a d3 overlay. It is largely based off this example from Mike Bostock: http://bl.ocks.org/mbostock/899711
I'm running it out of a Rails 4 app, and loading in the d3.js library from a CDN. So have access to d3.js and jQuery libraries and would prefer to avoid adding more libraries.
Task:
The data points(represented by svg:rect
elements) sometimes obscure the place names on the google map. I want it so that a user can pass their cursor over those datapoints and that they will momentarily pop out of the way.
I should note I'm trying to have the points reposition randomly, so can't really go the hover CSS route.
So...
Put an event listener on the SVG (or rect depending on what element should actually be manipulated)
Write a callback function that repositions (offsets? animates? transitions?) the element that triggered the event
Waits for a setTimeout (or uses the d3 .each("end",function())
) and then returns the element to it's prior position
Simple right?
Attempts:
So, attempted with d3 and then tried with jQuery. Bailed on the jQuery, since it doesn't seem to handle SVG (although I may have to resort to using the jQuery-SVG library if d3.js doesn't come through with the goods)
With the d3 attempt, the event handler assignment is fine. I have assigned the handler to the svg parent element, not the rect child element.
.on('mouseover', scatter)
This produces the expected behaviour of the event triggering and the scatter function being called when I mouse over.
However, I haven't been able to get any sensible articulation of the behaviour I am looking for.
I've tried changing the x,y values. I've tried changing the top and left values. The values seem to change on the object but nothing moves. I've tried those with straight d3 style selection and also with d3 style transitions.
I've tried the same changes on the rect child element. This moves the rect element but does so without bring the SVG along with it, so it doesn't display.
Relevant Code:
Appending SVGs to every data point, setting the correct x and y for the SVG and adding an event listener to each.
var marker = layer.selectAll("svg")
.data(data)
.each(transformMarker)
.enter().append("svg:svg")
.each(transformMarker)
.on('mouseover', scatter);
Separately, I style the SVG parent elements to contain a rect child element, and style the rects, so something actually displays on the screen.
The transformMarker function, which you might need to make sense of how the marker is positioned.
function transformMarker(d) {
d = new google.maps.LatLng(J.lat(d), J.lon(d));
d = projection.fromLatLngToDivPixel(d);
return d3.select(this)
.style("left", (d.x) + "px")
.style("top", (d.y) + "px");
The scatter function (with a boatload of console.logs so you can see what is happening)
function scatter(d){
console.log("the event has fired")
d = new google.maps.LatLng(J.lat(d), J.lon(d));
d = projection.fromLatLngToDivPixel(d);
console.log(d.x)
console.log((d.x + 15) + "px")
console.log(this)
d3.select(this)
.attr("x", (d.x + 15))
.attr("y", (d.y + 15))
console.log(this)
And what the console.logs pump out
the event has fired
602.0032221842557
617.0032221842557px
<svg style="left: 602.0032221842557px; top: 230.00228557549417px;"
x="617.0032221842557" y="245.00228557549417">
<rect height="7" width="7" fill="#138770" stroke="#0f0f02" stroke-width="0.5"></rect>
</svg>
<svg style="left: 602.0032221842557px; top: 230.00228557549417px;"
x="617.0032221842557" y="245.00228557549417">
<rect height="7" width="7" fill="#138770" stroke="#0f0f02" stroke-width="0.5"></rect>
</svg>
And the CSS for the SVG, since that could be throwing spanners in the works
.overlay, .overlay svg {
position: absolute;
}
.wrapper, .overlay svg {
/*border: 1px solid black;*/
width: 7px;
height: 7px;
transform: translateZ(0px);
-webkit-transform: translateZ(0px);
-moz-transform: translateZ(0px);
}
I have a few days away from civilisation, so was hoping to lay this problem out there and come back with a fresh approach and some suggestions from Stack Overflow and the d3 google groups.
Please let me know if you have any other questions or need more information.