How is this nice D3.js scattergraph highlighting effect achieved? - javascript

I love the way this scatterchart highlights circles when you mouse over them: http://novus.github.com/nvd3/examples/scatterWithLegend.html
But there's a lot of code there (looks like the author has defined his/her own standard libraries) and I can't figure out exactly how the effect is achieved.
Is it something to do with the .hover class and the stroke-width property?
I'd like to achieve the same effect on my own scatter graph, although I'm using circles rather than paths, so it may not be possible.

In the example, the effect seems to be realised in scatter.js from line 136.
Just highlighting individual circles is much easier though and doesn't need all the other stuff the example does. All you need to do is add a mouseover handler to the circles and (for example) increase stroke-width. That would look something like
d3.selectAll("circle").data(...).enter()
.append(...)
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
I'm assuming that a CSS class highlight defines the style. Alternatively, you can just use (in this example) CSS class circle:hover without the need for the event handlers.

Related

D3 anywhere outside graph event listener

Basically I have a graph where the user can click different SVG shapes and by doing so a .on('click') function will change the color fills. However, at some point it's going to be important to give the user the option to revert the graph to its initial state. I think the intuitive way to go about this for my case is by clicking anywhere outside the graph, i.e somewhere on the document body that is not in the graph coordinate plane (margins, padding, ect).
I tried this:
d3.select('body').on('click', function() {
d3.selectAll('circle').style('fill', function(d) {
return d.color;
})
});
It did not have any effect. I'm guessing that my existing on click effects of the shapes are overriding my d3.select('body').on('click') that I tried above. Either that or my approach is just flat out wrong.
Any suggestions here would be great.
Also I am aware that .attr('fill') and .style('fill') should almost always be consistent throughout, I do need .style('fill') here. I tried .attr('fill') just to be safe as well.
You might be able to use something like
window.onclick = function(event){
if(!(event.target.className.baseVal=="circleClass")){
d3.selectAll('circle').style('fill', function(d) {
return d.color;
})
}
}

Concentric colours on D3 Sunburst diagram

I have a Sunburst diagram which uses essentially the same code as the standard at http://bl.ocks.org/kerryrodden/7090426 .
However, I have many many 'nodes' in my final two rings and any combination of colours makes it look extremely messy. As each node within the diagram is pulled from a database I'm unable to assign specific colours to values as the values are all unique.
Is there a way that I could specify a colour for the entirety of each individual ring in the diagram? As an example, I would like it to look somewhat like this:
http://www.design-by-izo.com/wp-content/uploads/2011/02/Krakow-3.jpg
That way I would be able to come up with a palette that doesn't clash as much as applying range of colours that d3 just cycles through.
From what I understand, you can simply change the fill style in the diagram, using depth of the respective data, like so:
.style('fill', function (d) {
return color(d.depth);
})
where color is some sort of a color array.
Alternatively, ES6/2015, just:
.style('fill', d => color(d.depth))
Here's a fiddle, showing you the effect: Fiddle
(based on this)
I hope this is what you want.

raphaeljs default attributes

The default stroke for creating SVG elements with raphaeljs seems to be black 1px. I can manually turn it off every time I create an element, but I rather set it as a default attribute "stroke: none" to the entire paper. Is it possible?
I encountered this problem a little while ago; I believe this is a library default. Whilst you could change this in your library source, doing so would make updating library versions difficult, so you might be better off disabling it in the code that calls Raphael.
If you're worried about this being verbose, you could use a delegate function that hides the 'defaulting hacks' as you build similar shapes.
It depends if this is just for defaults, or trying to cut down repeated code etc. You could create your own shape with defaults you want as a possible alternative is what I was thinking...
var paper = Raphael('mydiv',400,400);
Raphael.fn.myBlueCircle = function (x,y,r) {
this.circle(x,y,r).attr({fill: "#00f", stroke: "none"});
};
paper.myBlueCircle(100,100,100);
paper.myBlueCircle(150,200,100);
jsfiddle
You may also want to do the same for sets if those are to be used with the new elements reference

Bring <path> to front on hover (when obscured by other element)

I was looking at the NYTimes interactive on state subsidies this morning and noticed that even when a state is obscured by a dot it is brought forward on hover.
For example the dot covering Massachusetts also partially covers New Hampshire, yet when you when you mouse into the covered part of New Hampshire, New Hampshire is brought forward.
How do you suppose they achieve this? The dots are in front of the state outlines based on their order in the DOM. I thought there might be a second set of state outlines on top of everything, listening for mouseovers that would trigger the underlying shape, but that doesn't seem to be the case.
I need to implement similar functionality in an application I'm working on and am curious about an elegant way with SVG elements.
Thanks.
As noted by Andy the circles in that NYT graphic have a CSS pointer-events property of none:
circle {
pointer-events: none;
}
the bring to front behavior can be achieved in a the mouseover function using this method:
stateShape.on("mouseover",function(){this.parentNode.appendChild(this);})
Now you can just use the raise() method:
Example in a function:
highlightSite(site) {
let selectedSite = d3.selectAll(`[siteName=${site}]`)
selectedSite.raise()
selectedSite
.attr('stroke', 'black')
.style('opacity', 1)
}

Ideas for rendering HTML *within* Raphael (SVG/VML) shapes

I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.
FWIW - I'm using Backbone.js views to modularize the shape logic.
Approach 1
My initial thought was to use a combination of foreignObject for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject, and therefore this approach had to be abandoned.
Approach 2
With the beside the canvas object, add divs that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div. There are a couple of problems with this approach:
Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?
Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support pointer-events, this is easily done:
div.shape-text-overlay {
position: absolute;
background: none;
pointer-events: none;
}
However, other browsers (like IE8 and below) need forwarding of the events:
var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
this.$elText.on(forwardedEvents, function(e) {
var original = e.originalEvent;
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent(e.type, true, true);
}
else {
event = document.createEventObject();
event.eventType = e.type;
}
// FYI - This is the most simplistic approach to event forwarding.
// My real implementation is much larger and uses MouseEvents and UIEvents separately.
event.eventName = e.type;
_.extend(event, original);
if (document.createEvent) {
that.el.node.dispatchEvent(event);
}
else {
that.el.node.fireEvent('on' + event.eventType, event);
}
});
Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:
This approach is what I'm currently using but it just feels like a huge hack.
Approach 3
Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.
Question
Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?
I built this project (no longer live) using Raphael. What I did is actually abandoned the idea of using HTML inside of SVG because it was just too messy. Instead, I absolutely positioned an HTML layer on top of the SVG layer and moved them around together. When I wanted the HTML to show, I merely faded it in and faded the corresponding SVG object out. If timed and lined up correctly, it's not really noticeable to the untrained eye.
While this may not necessarily be what you're looking for, maybe it will get your mind thinking of new ways to approach your problem! Feel free to look at the JS on that page, as it is unminified ;)
PS, the project is a lead-gathering application. If you just want to see how it works, select "Friend" in the first dropdown and you don't have to provide any info.
Unless another answer can be provided and trump my solution, I have continued with the extra div layer. Forwarding events was the most difficult part (if anyone requires the forwarding events code, I can post it here). Again, the largest downside to this solution is that overlapping shapes will cause their text to overlap above the actual drawn shape.

Categories

Resources