Highcharts - drill down to multiple series

highcharts, javascript

Solution

You can refer this demo: JSFIDDLE

code:

$(function () {
var chart;
$(document).ready(function() {

    var colors = Highcharts.getOptions().colors,
        categories = ['MSIE', 'Firefox', 'Chrome', 'Safari', 'Opera'],
        name = ['Browser brands'],
        data = [{
                y: 55.11,
                color: colors[0],
                drilldown: {
                    categories: ['MSIE 6.0', 'MSIE 7.0', 'MSIE 8.0', 'MSIE 9.0'],
                    series: [{
                        type: 'spline',
                        name: 'MSIE versions 2000',
                        data: [10.85, 7.35, 33.06, 2.81],
                        color: colors[0]
                    },{
                        type: 'spline',
                        name: 'MSIE versions 2010',
                        data: [1, 5, 10, 15],
                        color: colors[0]
                    }]
                }
            }, {
                y: 21.63,
                color: colors[1],
                drilldown: {
                    name: 'Firefox versions',
                    categories: ['Firefox 2.0', 'Firefox 3.0', 'Firefox 3.5', 'Firefox 3.6', 'Firefox 4.0'],
                    data: [0.20, 0.83, 1.58, 13.12, 5.43],
                    color: colors[1]
                }
            }, {
                y: 11.94,
                color: colors[2],
                drilldown: {
                    name: 'Chrome versions',
                    categories: ['Chrome 5.0', 'Chrome 6.0', 'Chrome 7.0', 'Chrome 8.0', 'Chrome 9.0',
                        'Chrome 10.0', 'Chrome 11.0', 'Chrome 12.0'],
                    data: [0.12, 0.19, 0.12, 0.36, 0.32, 9.91, 0.50, 0.22],
                    color: colors[2]
                }
            }, {
                y: 7.15,
                color: colors[3],
                drilldown: {
                    name: 'Safari versions',
                    categories: ['Safari 5.0', 'Safari 4.0', 'Safari Win 5.0', 'Safari 4.1', 'Safari/Maxthon',
                        'Safari 3.1', 'Safari 4.1'],
                    data: [4.55, 1.42, 0.23, 0.21, 0.20, 0.19, 0.14],
                    color: colors[3]
                }
            }, {
                y: 2.14,
                color: colors[4],
                drilldown: {
                    name: 'Opera versions',
                    categories: ['Opera 9.x', 'Opera 10.x', 'Opera 11.x'],
                    data: [ 0.12, 0.37, 1.65],
                    color: colors[4]
                }
            }];

    function setChart(name, categories, data, color, type) {
        var len = chart.series.length;
        chart.xAxis[0].setCategories(categories);
        for(var i = 0; i < len; i++){
            console.log(chart.series.length);
            chart.series[0].remove();
        }
        console.log('a');
        if(data.series){
            for(var i = 0; i < data.series.length; i ++ ){
                chart.addSeries({
                    name: data.series[i].name,
                    data: data.series[i].data,
                    type: data.series[i].type,
                    color: data.series[i].color || 'white'
                });
            }
        } else {
                chart.addSeries({
                    name: name,
                    data: data,
                    type: type,
                    color: color || 'white'
                });
        }
    }

    chart = new Highcharts.Chart({
        chart: {
            renderTo: 'container',
            type: 'column'
        },
        title: {
            text: 'Browser market share, April, 2011'
        },
        subtitle: {
            text: 'Click the columns to view versions. Click again to view brands.'
        },
        xAxis: {
            categories: categories
        },
        yAxis: {
            title: {
                text: 'Total percent market share'
            }
        },
        plotOptions: {
            column: {
                cursor: 'pointer',
                point: {
                    events: {
                        click: function() {
                            var drilldown = this.drilldown;
                            if (drilldown) { // drill down
                                setChart(null, drilldown.categories, drilldown, drilldown.type);
                            } else { // restore
                                setChart(name, categories, data, drilldown.type);
                            }
                        }
                    }
                },
                dataLabels: {
                    enabled: true,
                    color: colors[0],
                    style: {
                        fontWeight: 'bold'
                    },
                    formatter: function() {
                        return this.y +'%';
                    }
                }
            },
            spline: {
                cursor: 'pointer',
                point: {
                    events: {
                        click: function() {
                            setChart(name, categories, data, 'column');
                        }
                    }
                },
                dataLabels: {
                    enabled: true,
                    color: colors[0],
                    style: {
                        fontWeight: 'bold'
                    },
                    formatter: function() {
                        return this.y + '%';
                    }
                }
            }
        },
        tooltip: {
            formatter: function() {
                var point = this.point,
                    s = this.x +':<b>'+ this.y +'% market share</b><br/>';
                if (point.drilldown) {
                    s += 'Click to view '+ point.category +' versions';
                } else {
                    s += 'Click to return to browser brands';
                }
                return s;
            }
        },
        series: [{
            name: name,
            data: data,
            color: 'white'
        }],
        exporting: {
            enabled: false
        }
    });
});

});

Problem

I have a parent chart that I need to drill down to a child chart with multiple series. Here is a toy example of what I would like to do. (I know this does not work) Notice I'm trying to pass in an array of ids to display in the child chart. Does anybody know a way to do this? If someone from Highcharts reads this, can this be added as a feature? ``` $(function () { // Create the chart $('#container').highcharts({ chart: { type: 'column' }, title: { text: 'Basic drilldown' }, xAxis: { type: 'category' }, plotOptions: { column : { stacking : 'normal' } }, series: [{ name: 'Yearly Orders', data: [{ name: 'Location 1', y: 100, drilldown: ['l1-wa', 'l1-wb'] }, { name: 'Location 2', y: 150, drilldown: ['l2-wa', 'l2-wb'] }] }], drilldown: { series: [{ name: 'Widget A', type: 'line', id: 'l1-wa', data: [ {name: 'Qtr 1', y: 5}, {name: 'Qtr 2', y: 25}, {name: 'Qtr 3', y: 25}, {name: 'Qtr 4', y: 20} ] },{ name: 'Widget B', type: 'line', id: 'l1-wb', data: [ {name: 'Qtr 1', y: 10}, {name: 'Qtr 2', y: 5}, {name: 'Qtr 3', y: 5}, {name: 'Qtr 4', y: 5} ] }, { name: 'Widget A', type: 'line', id: 'l2-wa', data: [ {name: 'Qtr 1', y: 25}, {name: 'Qtr 2', y: 5}, {name: 'Qtr 3', y: 5}, {name: 'Qtr 4', y: 15} ] },{ name: 'Widget B', type: 'line', id: 'l2-wb', data: [ {name: 'Qtr 1', y: 30}, {name: 'Qtr 2', y: 30}, {name: 'Qtr 3', y: 15}, {name: 'Qtr 4', y: 25} ] } ] } }) }); ``` Here is a JSFiddle of this drilling down to just one series http://jsfiddle.net/F7z3D/2/ EDIT - I extended Highcharts to give me some enhanced changes when drilling down. This is probably not the best code, but gets the point across. This allows for changing of the x and y axes, changing the subtitle, and multiple series. ``` $(function () { (function (H) { var noop = function () {}, defaultOptions = H.getOptions(), each = H.each, extend = H.extend, format = H.format, wrap = H.wrap, Chart = H.Chart, seriesTypes = H.seriesTypes, PieSeries = seriesTypes.pie, ColumnSeries = seriesTypes.column, fireEvent = HighchartsAdapter.fireEvent, inArray = HighchartsAdapter.inArray; H.wrap(H.Chart.prototype, 'drillUp', function (proceed) { var chart = this, drilldownLevels = chart.drilldownLevels, levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber, i = drilldownLevels.length, chartSeries = chart.series, seriesI = chartSeries.length, level, oldSeries, newSeries, oldExtremes, addSeries = function (seriesOptions) { var addedSeries; each(chartSeries, function (series) { if (series.userOptions === seriesOptions) { addedSeries = series; } }); addedSeries = addedSeries || chart.addSeries(seriesOptions, false); if (addedSeries.type === oldSeries.type && addedSeries.animateDrillupTo) { addedSeries.animate = addedSeries.animateDrillupTo; } if (seriesOptions === level.seriesOptions) { newSeries = addedSeries; } }; while (i--) { level = drilldownLevels[i]; console.log(level.levelNumber); console.log(levelNumber); if (level.levelNumber === levelNumber) { drilldownLevels.pop(); // Get the lower series by reference or id oldSeries = level.lowerSeries; if ($.isArray(oldSeries)) { if (chart.series) { while (chart.series.length > 0) { chart.series[0].remove(false); } } if (level.levelSubtitle) { chart.setTitle(null, {text: level.levelSubtitle}); } else { chart.setTitle(null, { text: '' }); } if (chart.xAxis && level.levelXAxis) { while (chart.xAxis.length > 0) { chart.xAxis[0].remove(false); } } if (chart.yAxis && level.levelYAxis) { while (chart.yAxis.length > 0) { chart.yAxis[0].remove(false); } } if (level.levelYAxis) { $.each(level.levelYAxis, function () { chart.addAxis(this, false, false); }); } if (level.levelXAxis) { $.each(level.levelXAxis, function () { chart.addAxis(this, true, false); }); } $.each(level.levelSeriesOptions, function () { chart.addSeries(this, false); }); } else { if (!oldSeries.chart) { // #2786 while (seriesI--) { if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id) { oldSeries = chartSeries[seriesI]; break; } } } oldSeries.xData = []; // Overcome problems with minRange (#2898) each(level.levelSeriesOptions, addSeries); fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions }); if (newSeries.type === oldSeries.type) { newSeries.drilldownLevel = level; newSeries.options.animation = chart.options.drilldown.animation; if (oldSeries.animateDrillupFrom) { oldSeries.animateDrillupFrom(level); } } newSeries.levelNumber = levelNumber; oldSeries.remove(false); // Reset the zoom level of the upper series if (newSeries.xAxis) { oldExtremes = level.oldExtremes; newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false); newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false); } } } } this.redraw(); if (this.drilldownLevels.length === 0) { console.log('destroy'); this.drillUpButton = this.drillUpButton.destroy(); } else { console.log('no destroy '+this.drilldownLevels.length); this.drillUpButton.attr({ text: this.getDrilldownBackText() }) .align(); } }); H.wrap(H.Chart.prototype, 'addSingleSeriesAsDrilldown', function (proceed, point, ddOptions) { if (!ddOptions.series) { proceed.call(this, point, ddOptions); } else { var thisChart = this; var oldSeries = point.series, xAxis = oldSeries.xAxis, yAxis = oldSeries.yAxis, color = point.color || oldSeries.color, pointIndex, levelSeries = [], levelSeriesOptions = [], levelXAxis = [], levelYAxis = [], levelSubtitle, level, levelNumber; levelNumber = oldSeries.levelNumber || 0; // ddOptions.series[0] = extend({ // color: color // }, ddOptions.series[0]); // pointIndex = inArray(point, oldSeries.points); // Record options for all current series each(oldSeries.chart.series, function (series) { if (series.xAxis === xAxis) { levelSeries.push(series); levelSeriesOptions.push(series.userOptions); series.levelNumber = series.levelNumber || 0; } }); each(oldSeries.chart.xAxis, function (xAxis) { levelXAxis.push(xAxis.userOptions); }); each(oldSeries.chart.yAxis, function (yAxis) { levelYAxis.push(yAxis.userOptions); }); if(oldSeries.chart.subtitle && oldSeries.chart.subtitle.textStr){ levelSubtitle = oldSeries.chart.subtitle.textStr; console.log(levelSubtitle); } // Add a record of properties for each drilldown level level = { levelNumber: levelNumber, seriesOptions: oldSeries.userOptions, levelSeriesOptions: levelSeriesOptions, levelSeries: levelSeries, levelXAxis: levelXAxis, levelYAxis: levelYAxis, levelSubtitle: levelSubtitle, shapeArgs: point.shapeArgs, bBox: point.graphic.getBBox(), color: color, lowerSeriesOptions: ddOptions, pointOptions: oldSeries.options.data[pointIndex], pointIndex: pointIndex, oldExtremes: { xMin: xAxis && xAxis.userMin, xMax: xAxis && xAxis.userMax, yMin: yAxis && yAxis.userMin, yMax: yAxis && yAxis.userMax } }; // Generate and push it to a lookup array if (!this.drilldownLevels) { this.drilldownLevels = []; } this.drilldownLevels.push(level); level.lowerSeries = []; if (ddOptions.subtitle) { this.setTitle(null, {text: ddOptions.subtitle}); }else{ this.setTitle(null, {text: ''}); } if (this.xAxis && ddOptions.xAxis) { while (this.xAxis.length > 0) { this.xAxis[0].remove(false); } } if (this.yAxis && ddOptions.yAxis) { while (this.yAxis.length > 0) { this.yAxis[0].remove(false); } } if (ddOptions.yAxis) { if ($.isArray(ddOptions.yAxis)) { $.each(ddOptions.yAxis, function () { thisChart.addAxis(this, false, false); }); } else { thisChart.addAxis(ddOptions.yAxis, false, false); } } if (ddOptions.xAxis) { if ($.isArray(ddOptions.xAxis)) { $.each(ddOptions.xAxis, function () { thisChart.addAxis(this, true, false); }); } else { thisChart.addAxis(ddOptions.xAxis, true, false); } } $.each(ddOptions.series, function () { var newSeries = thisChart.addSeries(this, true); level.lowerSeries.push(newSeries); newSeries.levelNumber = levelNumber + 1; // if (xAxis) { // xAxis.oldPos = xAxis.pos; // xAxis.userMin = xAxis.userMax = null; // yAxis.userMin = yAxis.userMax = null; // } // // Run fancy cross-animation on supported and equal types // if (oldSeries.type === newSeries.type) { // newSeries.animate = newSeries.animateDrilldown || noop; // newSeries.options.animation = true; // } }); } }); H.wrap(H.Point.prototype, 'doDrilldown', function (proceed, _holdRedraw) { if (!$.isPlainObject(this.drilldown)) { proceed.call(this, _holdRedraw); } else { var ddChartConfig = this.drilldown; console.log(ddChartConfig); var ddSeries = ddChartConfig.series; var series = this.series; var chart = series.chart; var drilldown = chart.options.drilldown; var seriesObjs = []; for (var i = 0; i < ddSeries.length; i++) { if (!$.isPlainObject(ddSeries[i])) { console.log(ddSeries[i]); var j = (drilldown.series || []).length; var seriesObj = null; while (j-- && !seriesObj) { if (drilldown.series[j].id === ddSeries[i]) { seriesObj = drilldown.series[j]; } } if (seriesObj) { seriesObjs.push(seriesObj); } } else { seriesObjs.push(ddSeries[i]); } } ddChartConfig.series = seriesObjs; ddSeries = ddChartConfig.series; // Fire the event. If seriesOptions is undefined, the implementer can check for // seriesOptions, and call addSeriesAsDrilldown async if necessary. HighchartsAdapter.fireEvent(chart, 'drilldown', { point: this, seriesOptions: ddChartConfig }); if (ddChartConfig) { if (_holdRedraw) { chart.addSingleSeriesAsDrilldown(this, ddChartConfig); } else { chart.addSeriesAsDrilldown(this, ddChartConfig); } } } }); }(Highcharts)); // Create the chart $('#container').highcharts({ chart: { type: 'column' }, title: { text: 'Basic drilldown' }, xAxis: { type: 'category' }, plotOptions: { column: { stacking: 'normal' } }, series: [{ name: 'Yearly Orders', data: [{ name: 'Location 1', y: 100, drilldown: { series: ['l1-wa', 'l2-wa'], subtitle: "subtitle", xAxis: { title: { text: 'X Axis' } }, yAxis: { title: { text: 'Y Axis' } } } }, { name: 'Location 2', y: 150 //, // drilldown: 'l2-wa' }] }], drilldown: { series: [{ name: 'Widget A', type: 'column', id: 'l1-wa', data: [{ name: 'Qtr 1', y: 5, drilldown: {series: ['l2-wb'], xAxis: { }, yAxis: { }} }, { name: 'Qtr 2', y: 25 }, { name: 'Qtr 3', y: 25 }, { name: 'Qtr 4', y: 20 }] }, { name: 'Widget B', type: 'line', id: 'l1-wb', data: [{ name: 'Qtr 1', y: 10 }, { name: 'Qtr 2', y: 5 }, { name: 'Qtr 3', y: 5 }, { name: 'Qtr 4', y: 5 }] }, { name: 'Widget A', type: 'column', id: 'l2-wa', data: [{ name: 'Qtr 1', y: 25 }, { name: 'Qtr 2', y: 5 }, { name: 'Qtr 3', y: 5 }, { name: 'Qtr 4', y: 15 }] }, { name: 'Widget B', type: 'pie', id: 'l2-wb', data: [{ name: 'Qtr 1', y: 30 }, { name: 'Qtr 2', y: 30 }, { name: 'Qtr 3', y: 15 }, { name: 'Qtr 4', y: 25 }] } ] } }) }); ``` Here is a JSFIDDLE http://jsfiddle.net/skTHx/10/

Original source

Related problems