Friday, 9 May 2014

Show / hide a d3.js element by clicking on another element

The following post is a portion of the D3 Tips and Tricks book which is free to download. To use this post in context, consider it with the others in the blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------
Show / hide an element by clicking on another element
This is a trick that I found I wanted to implement in order to present a graph with a range of lines and to then provide the reader with the facility to click on the associated legend to toggle the visibility of the lines off and on as required.
The example we’ll follow is our friend from earlier, a slightly modified example of the graph with two lines.
Show / hide lines on a graph
In this example we will be able to click on either of the two titles at the bottom of the graph (‘Blue Line’ or ‘Red Line’) and have it toggle the respective line and Y axis.
THE CODE
The code for the example is available online at bl.ocks.org or GitHub. It is also available as the file ‘show-hide.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.
There are two main parts to implementing this technique. Firstly we have to label the element (or elements) that we wish to show / hide and then we have to give the object that will get clicked on the attribute that allows it to recognize a mouse click and the code that it subsequently uses to show / hide our labelled element.
Labeling the element that is to be switched on and off is dreadfully easy. It simply involves including an idattribute to an element that identifies it uniquely.
svg.append("path")
    .attr("class", "line")
    .attr("id", "blueLine")
    .attr("d", valueline(data));
In the example above we have applied the id blueLine to the path that draws the blue line on our graph.
The second part is a little trickier. The following is the portion of JavaScript that places our text label under the graph. The only part of it that is unusual is the .on("click", function() section of the code.
svg.append("text")
    .attr("x", 0)             
    .attr("y", height + margin.top + 10)    
    .attr("class", "legend")
    .style("fill", "steelblue")         
    .on("click", function(){
        // Determine if current line is visible
        var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;
        // Hide or show the elements
        d3.select("#blueLine").style("opacity", newOpacity);
        d3.select("#blueAxis").style("opacity", newOpacity);
        // Update whether or not the elements are active
        blueLine.active = active;
When we click on our ‘Blue Line’ text element the .on("click", function() section executes.
We’re using a short-hand version of the if statement a couple of times here. Firstly we check to see if the variable blueLine.active is true or false and if it’s true it gets set to false and if it’s false it gets set to true (not at all confusing).
        var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;
Then after toggling this variable we set the value of newOpacity to either 0 or 1 depending on whether active isfalse or true (the second short-hand JavaScript if statement).
We can then select our identifiers that we have declared using the id attributes in the earlier pieces of code and modify their opacity to either 0 (off) or 1 (on)
        d3.select("#blueLine").style("opacity", newOpacity);
        d3.select("#blueAxis").style("opacity", newOpacity);
Lastly we update our blueLine.active variable to whatever the active state is so that it can toggle correctly the next time it is clicked on.
        blueLine.active = active;
Quite a neat piece of code. Kudos to Max Leiserson for providing the example on which it is largely based in an answer to a question on Stack Overflow.

The description above (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

10 comments:

  1. Demo is pointing to a wrong one.

    ReplyDelete
    Replies
    1. Yikes! Many thanks for point this out. It will be the same in the book as well. Fixed in the post above now and the revised edition of the book will be available in half an hour. Thanks again.

      Delete
  2. I'm not sure if title correctly shows article content. People are moved here from google and they more want to look at D3 pattern for hidding / showing simple divs...

    ReplyDelete
    Replies
    1. Hmm.... Interesting observation. Do you have an alternative suggestion?

      Delete
  3. What If I want to set a dynamic Id in active function to check whether its active and assigning it back the value of active to it. For example I have four lines in my graph and each created different Ids. I want to check the user clicked line id and set to active(true and false). I tried doing the same but its not working. It works only with static values I guess. Could you give me any workaround for the same?

    ReplyDelete
    Replies
    1. It sounds a little bit like what I explain in the post here http://www.d3noob.org/2014/07/d3js-multi-line-graph-with-automatic.html You can check out a live version here http://bl.ocks.org/d3noob/e99a762017060ce81c76
      This is actually one of my favourite graphs, just because I worked out how to dynamically assign id's. (I'm a slow learner :-)).

      Delete
  4. Whats the .active variable? Is it a custom attribute or in built d3 selection attr ? Please clarify.

    ReplyDelete
    Replies
    1. It's just a variable and the name is purely descriptive (i.e, we could call it anything). I'ts only role is to toggle between true and false. to test this (because I haven't, and it's been a loooong time since I did this example) try changing the variable name to something different.

      Delete
  5. I have lines with a tooltip and I am able to hide the lines but the tooltips do no go away so all I am left with is a graph with dots. I was wondering if you know a way to fix that?

    ReplyDelete
    Replies
    1. Ahh... Very good. What you would need to do is to change the opacity of all the objects associated with the legend label that is clicked on. To do that I think that you would need to have the tooltips associated with the line and use a selectall statement instead of a select. This might need a bit of experimentation. Good luck

      Delete