D3 Tips and Tricks v4

Sunday, 6 January 2013

Adding a drop shadow to allow text to stand out on graphics in a d3.js graph


The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf  and / or the examples from the downloads page:-)
--------------------------------------------------------

I've deliberately positioned this particular tip to follow the 'filling an area' description because it provides an opportunity to demonstrate the principle to slightly better effect.

There have been several opportunities where I have wanted to place text overlaid on graphs for convenience sake only to have it look overly messy as the text interferes with the graph.

Now, I'll be the first to say that the principle of overlaying text on a graph is probably not best practice, but sometimes you’ve got to do what you've got to do. Besides. Sometimes it's a valid idea. If I remember rightly, the first time I came across this idea, it was being used to highlight text when positioned on bars of a bar graph. So it's not always an evil practice :-).

Anyway, what we'll do is leave the fill in place and place the title back on the graph, but position the title so that it lays on top of the fill like so;
The additional code for the title is the following and appears just after the drawing of the axes.
svg.append("text")
        .attr("x", (width / 2))             
        .attr("y", 25 )
        .attr("text-anchor", "middle")  
        .style("font-size", "16px") 
        .style("text-decoration", "underline")  
        .text("Value vs Date Graph");
(the only change from the previous title example is the 'y' attribute which has been hard coded to 25 to place it inconveniently on the filled area.)

So, what we want to end up with is something like the following...
In my humble opinion, it's just enough to make the text acceptable :-).

The method that I'll describe to carry this out is designed so that the drop shadow effect can be applied to any of the text in a graph, not the isolated example that we will use here. In order to implement this marvel of utility we will need to make changes in two areas. One in the CSS where we will define a style for white shadowy backgrounds and the second to draw it.

CSS for white shadowy background

The code to add to the CSS section is as follows;
text.shadow {
    stroke: white;
    stroke-width: 2.5px;
    opacity: 0.9;
}
The first line designates that the style applies to text with a 'shadow' label. The stroke is set to white. the width of the line is set to 2.5px and it is made to be slightly see-through. So by setting the line that surrounds the text to be thick, white and see-through gives it a slightly 'cloudy' effect. If we remove the black text from over the top we get a slightly better look;


Of course if you want to have a play with any of these settings, you should have a go and see what works best for your graph.

Drawing the white shadowy background.

Now that we've set the style for our background, we need to draw it in.

The code for this should be extremely familiar;
svg.append("text")
        .attr("x", (width / 2))             
        .attr("y", 25 )
        .attr("text-anchor", "middle")  
        .style("font-size", "16px") 
        .style("text-decoration", "underline")  
        .attr("class", "shadow")        // <=== Here's the different line
        .text("Value vs Date Graph");

That's because it's identical to the piece of code that was used to draw the title except for the one line that is indicated above. The reason that it's identical is that what we are doing is placing a white shadow on the graph and then the text on top of it, if it deviated by a significant amount it will just look silly. Of course a slight amount could look effective, in which case adjust the 'x' or 'y' attributes.

One of the things I pointed out in the previous paragraph was extremely important. That's the bit that tells you that we needed to place the shadow before we placed the black text. For the same reason that we placed the area fill on first in the area fill example, If black text goes on before the shadow, it will look pretty silly. So place this block of code just before the block that draws the title.

So the line that has been added in is the one that tells D3 that the text that is being drawn will have the white cloudy effect. And at the risk of repeating myself, if you have several text elements that could benefit from this effect, once you have the CSS code in place, all you need to do is duplicate the block that adds the text and add in that single line and viola!

The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.

2 comments:

  1. But if you want to transform the text during an event (like on click), you can only access the first text added (the shadow text) with the selector. How do you then select the last text added (the top layer darker text) so that you can transform it (like highlight it)?

    ReplyDelete
    Replies
    1. Really good question! Tragically, I don't know off the top of my head and am a bit pressed for time for the next few weeks. But here's a couple of things I'd try. First, try assigning a class to the 'top' layer (the shadow has the '.attr("class", "shadow")' line in it). I wish I knew more about how that all works, but I'm a bit of a noob there as well sorry. The other thing I'd try is to remove the shadow layer and see if the text is selected properly then (That won't fix the problem, but it might indicate a place to start). The third thing I'd try is to perhaps group the two elements and act on both at the same time. Now this could be trickier, but I watched a great video by 'D3Vienno' here (https://www.youtube.com/watch?v=SYuFy1j8SGs) (check out his others, they're awesome) and I will have to give it a try. Sorry, I don't have a straight answer for you, but at least there's some places to start. If it all turns to custard, if you can post the code as a functioning gist, you could ask the question on stackoverflow? (this may be pretty involved, depending on your confidence)

      Delete