D3.js Multiple Line Plot – Part 6

Working version of the plot, download here.

This final post describes the code which needs to be executed after any time-series has been added or removed. When a time-series has been added or removed via the legend button, it is possible that its unique range of y-values is not currently displayed in the plot. For instance, assume the “52 Days” and “104 Days” time-series are currently displayed and the y-range is: [0 – 123]. Now, the “2520 Days” time-series is added by a mouse click. This time series has y-values in the range [420 – 540]. Thus, the new range [0 – 540] needs to be established as the new y-axis. However, all the time-series displayed “pre-click” (52 and 104 Days) will need to be removed and re-drawn since their paths are scaled based on the [0 – 123] range of y-values. As seen in part 4, if the legend button is black with a strike-through, the addWindow() function is called:

function addWindow(name, addAllBoolean, updateBoolean, longTransition) {
    var transition = longTransition ? 500 : 0;
    if (svg.selectAll(".index").empty() || !svg.selectAll(".index").classed("a" + name) || updateBoolean) {
        var idName = name.toString();
        var tempIndex = svg.selectAll(".aaa")
            .data(indices.filter(function(d) {
                return d.name == idName;
            }))
            .enter().append("g")
            .attr("id", function(d) {
                return "index" + d.name;
            })
            .attr("class", function(d) {
                return "index a" + d.name;
            });
        if (!addAllBoolean) {
            var displayedIndices = d3.selectAll(".index").data();
            y.domain([
                d3.min(displayedIndices, function(c) {
                    return d3.min(c.values, function(v) {
                        return v.yvalue;
                    });
                }),
                d3.max(displayedIndices, function(c) {
                    return d3.max(c.values, function(v) {
                        return v.yvalue;
                    });
                })
            ]);
            svg.select(".y.axis")
                .call(yAxis);
            d3.selectAll(".index").data().forEach(function(d) {
                removeWindow(d.name, false, false);
                (d.name == name) ? addWindow(d.name, true, true, true): addWindow(d.name, true, true, false);

            });
        }
        tempIndex.append("path")
            .attr("class", "line")
            .attr("d", function(d) {
                return line(d.values);
            })
            .style("opacity", 0.0)
            .style("stroke", function(d) {
                return color(d.name);
            })
            .transition()
            .style("opacity", 1.0)
            .duration(transition);
    }
}

As can be seen by the addWindow() function, in order to add a window, all previous windows need to be removed. If the window is currently displayed, it can be removed with a mouse click. There are two types of addWindow() and removeWindow() function calls. The logic is based on various boolean values. This is needed because of the “Add All” and “Remove All” buttons. These buttons require a different logic than the case of a single time-series being added or removed. Presented below is the removeWindow() function:

function removeWindow(name, longTransitionBoolean, firstClick) {
    var transition = longTransitionBoolean ? 500 : 0;
    var idName = "#index" + name.toString();
    d3.select(idName)
        .transition()
        .style("opacity", 0.0)
        .duration(transition)
        .remove().each("end", function() {
            if (firstClick) {
                var displayedIndices = d3.selectAll(".index").data();
                if (!d3.selectAll(".index").empty()) {
                    y.domain([
                        d3.min(displayedIndices, function(c) {
                            return d3.min(c.values, function(v) {
                                return v.yvalue;
                            });
                        }),
                        d3.max(displayedIndices, function(c) {
                            return d3.max(c.values, function(v) {
                                return v.yvalue;
                            });
                        })
                    ]);
                    svg.select(".y.axis")
                        .call(yAxis);
                    d3.selectAll(".index").data().forEach(function(d) {
                        removeWindow(d.name, false, false);
                        addWindow(d.name, true, true, false);

                    });
                    addMouse();
                }
            }
        });

In each of these functions, the D3 transition function is called in order to make the addition and remove of the time-series lines smooth.
This concludes the posts concerning drawing multiple line plots with mouse interaction in D3.js. Hope this helps!