0
votes

I've been asked to add an interactive legend to my Multi-line chart. My Multi-line chart has a nest with two keys and it made things difficult for my legend. Here's an old EAMPLE I'm using for adding an interactive legend.

My issues:

1) I'm trying to display the top level key for the legend so that it shows system01, system02, system03 and so on instead of displaying the two keys together. For example, system01 0 and systmem01 2 become system01 so when it is clicked it will hide both lines.

2) After making the lines inactive, the tooltip still displays when mousing over the chart.

Here's my FIDDLE

Snippet for adding the Legend:

var chartBody = svg.append("g")
    .attr("class", "chartbody")
    .attr("clip-path", "url(#clip)");

for (var key in storages) {
    if (storages.hasOwnProperty(key)) {
        console.log("key:", key, [storages[key]]);
        var capSeries = d3.line()
            .x(function(d) {
                return x(d.date); })
            .y(function(d) {
                return y(d.availableVolumeCapacity); })
            .curve(d3.curveCardinal);
        // mapping of data to line using the capSeries function
        chartBody.append("path")
            .data([storages[key]])
            .attr("class", "line")
            .attr("id", 'tag'+ key.replace(/\s+/g, ''))
            .attr("d", capSeries)
            .style("stroke", z(key));
    }
}

// spacing for the legend
legendSpace = width/nest.length;

// Loop through each symbol / key
nest.forEach(function(d,i) {

// Add the Legend
    svg.append("text")
        .attr("x", (legendSpace/2)+i*legendSpace)  // space legend
        .attr("y", height + (margin.bottom/2)+ 5)
        .attr("class", "legend")    // style the legend
        .style("fill", function() { // Add the colours dynamically
            return d.z = z(d.key); })
        .on("click", function(){
            // Determine if current line is visible
            var active = d.active ? false : true,
            newOpacity = active ? 0 : 1;
            // Hide or show the elements based on the ID
            d3.select("#tag"+d.key.replace(/\s+/g, ''))
                .transition().duration(100)
                .style("opacity", newOpacity);
            // Update whether or not the elements are active
            d.active = active;
            })
        .text(d.key);

});
1
I've solved your issue no2, didn't understand issue no 1, well see this link - saikiran.vsk
I apologize for not explaining myself clearly. I'm looking to group the subkeys for the legend. For example system01 0 and systmem01 2 become system01 so when it is clicked it will hide both lines. - Clarkus978
@Clarkus978 you can just change the displayed text and it will work fine, take a look at my answer - mkaran
@mkaran thank you for your answer, but that doesn't solve the issue. I'm looking to group system01 0 and system01 02 so that it only displays a single system01 in the legend. When system01 is clicked on it will hide lines system01 0 and system01 2. - Clarkus978
@Clarkus978 oh, I see! thanks for the clarification - mkaran

1 Answers

1
votes

Take a look at this: https://jsfiddle.net/mkwne5uh/

For 2) A simple solution would be to keep track of the hidden and apply mouseover and mouseout conditionally.

// define an array to hold the hidden info
var hidden = [];
...
...
 // Loop through each symbol / key
    nest.forEach(function(d,i) {

    // Add the Legend
        svg.append("text")
            .attr("x", (legendSpace/2)+i*legendSpace)  // space legend
            .attr("y", height + (margin.bottom/2)+ 5)
            .attr("class", "legend")    // style the legend
            .style("fill", function() { // Add the colours dynamically
                return d.z = z(d.key); })
            .on("click", function(){

                // Update whether or not the elements are active
                // initial d.active will be undefined, so on first click
                // d.active will become false like this
                d.active = !d.active;  
                // Determine if current line is visible
                newOpacity = d.active ? 0 : 1;

                // if category is not active now, remove it
                if(!d.active){
                    hidden.splice(hidden.indexOf(d.key), 1);
                }
                else{// keep it for use later on
                    hidden.push(d.key)
                }
                // Hide or show the elements based on the ID
                d3.select("#tag"+d.key.replace(/\s+/g, ''))
                    .transition().duration(100)
                    .style("opacity", newOpacity);
                })
            .text(d.key);
    });

and then:

function mouseover(d) {
    if( hidden.indexOf(d.data.storageSystem + " " + d.data.poolId)> -1) return;
....
function mouseout(d) {
        if( hidden.indexOf(d.data.storageSystem + " " + d.data.poolId)> -1) return;

As for 1) you could use .text(d.key.split(" ")[0]) but I am not so sure it would be the best solution.

Hope this helps, good luck!