You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

dc.js技术咨询:可缩放Sunburst、非交互图表及多级MenuSelect问题

Hey there! Let's tackle your dc.js questions one by one—happy to help a fellow newbie get up to speed with this flexible library.

1. Implementing Zoomable Sunburst with dc.sunburst

Absolutely! dc.js's sunburst chart is built on top of D3.js, so we can extend it with D3's zoom behavior to get that interactive, zoomable effect. Here's a complete example:

First, set up your basic chart structure, then add the zoom logic in a renderlet (which runs after the chart renders):

<div id="sunburst-chart"></div>

<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/4.2.0/dc.min.js"></script>
// Sample hierarchical data (adjust to your structure)
const data = {
  name: 'Root',
  children: [
    {
      name: 'Bâle',
      children: [
        { name: 'A1', value: 10 },
        { name: 'A2', value: 20 }
      ]
    },
    {
      name: 'Zurich',
      children: [
        { name: 'B1', value: 15 },
        { name: 'B2', value: 25 }
      ]
    }
  ]
};

// Convert data to crossfilter-compatible format (flattened)
function flattenData(node, parent = '') {
  let flat = [];
  const path = parent ? `${parent}.${node.name}` : node.name;
  if (node.children) {
    node.children.forEach(child => {
      flat = flat.concat(flattenData(child, path));
    });
  } else {
    flat.push({ path: path, value: node.value });
  }
  return flat;
}

const flatData = flattenData(data);
const cf = crossfilter(flatData);
const pathDimension = cf.dimension(d => d.path.split('.'));
const pathGroup = pathDimension.group().reduceSum(d => d.value);

// Create sunburst chart
const sunburstChart = dc.sunburstChart('#sunburst-chart')
  .width(600)
  .height(600)
  .dimension(pathDimension)
  .group(pathGroup)
  .valueAccessor(d => d.value);

// Add D3 zoom behavior
sunburstChart.on('renderlet', function(chart) {
  const svg = chart.select('svg');
  const g = svg.select('g');

  // Define zoom function
  function zoom(event) {
    g.attr('transform', event.transform);
  }

  // Apply zoom to SVG
  svg.call(d3.zoom()
    .scaleExtent([1, 8])
    .on('zoom', zoom));
});

// Render the chart
dc.renderAll();

The key here is using D3's zoom() behavior in the renderlet to enable scaling and panning of the sunburst. Adjust the scaleExtent to control how far users can zoom in/out.

2. Make a Chart Respond to Filters But Not Trigger Them

To set up a chart that only reacts to other charts' filters (but doesn't send its own filter events to the rest of the dashboard), you need to override its filterHandler to avoid modifying the Crossfilter dimension when users interact with it. Here's how:

// Example with a bar chart (works for any dc chart type)
const passiveChart = dc.barChart('#passive-chart')
  .dimension(yourDimension)
  .group(yourGroup)
  // Override filterHandler to skip applying filters to the dimension
  .filterHandler(function(dimension, filters) {
    // We return the filters so the chart updates itself, but don't apply them to the dimension
    // This means other charts won't get notified of changes from this chart
    return filters;
  });

If you want the chart to still highlight filtered values but not propagate the filter, this approach works perfectly. The chart will still update when other charts apply filters (since it's bound to the same dimension/group), but clicks on this chart won't affect the rest of your dashboard.

If you also want to disable click interactions entirely (but keep responsiveness to filters), you can add:

passiveChart.interactive(false);
3. Create a Nested (Two-Level) Dropdown with dc.menuSelect

dc.menuSelect doesn't support nested optgroups out of the box, but we can manually build the dropdown structure using a renderlet to replace the default flat options. Here's a step-by-step example:

First, prepare your data and Crossfilter setup:

// Sample 2D data
const data = [
  { city: 'Bâle', zone: 'A1', value: 10 },
  { city: 'Bâle', zone: 'A2', value: 20 },
  { city: 'Zurich', zone: 'B1', value: 15 },
  { city: 'Zurich', zone: 'B2', value: 25 },
  { city: 'Geneva', zone: 'C1', value: 30 }
];

const cf = crossfilter(data);
// Create a dimension that uses both city and zone as a key array
const cityZoneDimension = cf.dimension(d => [d.city, d.zone]);
const cityZoneGroup = cityZoneDimension.group().reduceSum(d => d.value);

// Group the data by city for nested dropdown
const groupedOptions = {};
cityZoneGroup.all().forEach(d => {
  const [city, zone] = d.key;
  if (!groupedOptions[city]) {
    groupedOptions[city] = [];
  }
  groupedOptions[city].push({ zone: zone, count: d.value });
});

Now build the menu select and customize its options:

const nestedMenu = dc.menuSelect('#nested-menu')
  .dimension(cityZoneDimension)
  .group(cityZoneGroup)
  .on('renderlet', function(chart) {
    const select = chart.select('select');
    // Clear default flat options
    select.html('');

    // Add "All" option
    select.append('option')
      .attr('value', '')
      .text('All');

    // Add optgroups for each city, with child options for zones
    Object.entries(groupedOptions).forEach(([city, zones]) => {
      const optgroup = select.append('optgroup')
        .attr('label', city);

      zones.forEach(zone => {
        optgroup.append('option')
          .attr('value', `${city}, ${zone.zone}`) // Match the flat key format
          .text(`${zone.zone} (${zone.count})`);
      });
    });
  });

dc.renderAll();

This will generate a dropdown with <optgroup> elements for each city, containing <option> elements for their respective zones. When a user selects an option, it will still filter the Crossfilter dimension correctly because we're using the same key format ("Bâle, A1") as your original flat setup.


内容的提问来源于stack exchange,提问作者Brice Repond

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:24