Filter by a Product in JavaScript

Using ApexCharts with JavaScript

This Filter by a Product example uses ApexCharts.js directly in JavaScript, with no wrapper component.

Install it with npm install apexcharts, then mount the chart with new ApexCharts(element, options).render().

JavaScript installation guide
// Shared by the vanilla-js, React and Vue builds.
//
// Revenue ($k) per product, region and quarter. The left chart sums it per
// product; the right one per region and quarter. Clicking a product filters
// the right chart BY it without dropping anything: every column keeps its
// full revenue, faded, and the product's share is drawn solid in front.
var PRODUCTS = ['Laptops', 'Phones', 'Tablets', 'Audio']
var REGIONS = ['North', 'South']
var QUARTERS = ['Q1', 'Q2', 'Q3', 'Q4']
var REVENUE = {
  Laptops: { North: [42, 38, 45, 61], South: [30, 28, 33, 44] },
  Phones: { North: [55, 49, 52, 70], South: [48, 51, 47, 66] },
  Tablets: { North: [18, 15, 17, 26], South: [22, 19, 21, 25] },
  Audio: { North: [12, 14, 13, 21], South: [9, 11, 10, 16] },
}

function sum(a) {
  return a.reduce(function (s, v) {
    return s + v
  }, 0)
}
function productTotal(p) {
  return sum(
    REGIONS.map(function (r) {
      return sum(REVENUE[p][r])
    }),
  )
}
function regionQuarters(r, onlyProduct) {
  return QUARTERS.map(function (q, k) {
    return sum(
      PRODUCTS.filter(function (p) {
        return !onlyProduct || p === onlyProduct
      }).map(function (p) {
        return REVENUE[p][r][k]
      }),
    )
  })
}

// The picked product, or null.
var picked = null

function productSeries() {
  var totals = PRODUCTS.map(productTotal)
  var s = { name: 'Revenue', data: totals }
  // On the source chart the pick is the whole of its own column, so it stays
  // solid while the others fade.
  if (picked) {
    s.highlightData = PRODUCTS.map(function (p, j) {
      return p === picked ? totals[j] : null
    })
  }
  return [s]
}

function quarterSeries() {
  return REGIONS.map(function (r) {
    var s = { name: r, data: regionQuarters(r) }
    if (picked) s.highlightData = regionQuarters(r, picked)
    return s
  })
}

function pickProduct(index) {
  var product = PRODUCTS[index]
  picked = picked === product ? null : product
  var readout = document.getElementById('readout')
  if (readout) {
    readout.innerHTML = picked
      ? 'Filtered by <b>' + picked + '</b>. Click it again to clear.'
      : 'Click a product to filter the quarters by it.'
  }
  // Let the click handler unwind before both charts redraw.
  Promise.resolve().then(function () {
    ApexCharts.getChartByID('products').updateSeries(productSeries())
    ApexCharts.getChartByID('quarters').updateSeries(quarterSeries())
  })
}

var options = {
  series: productSeries(),
  chart: {
    id: 'products',
    type: 'bar',
    height: 320,
    toolbar: { show: false },
    events: {
      dataPointSelection: function (e, ctx, opts) {
        pickProduct(opts.dataPointIndex)
      },
    },
  },
  colors: ['#0f766e'],
  plotOptions: {
    bar: { horizontal: true, barHeight: '64%', borderRadius: 3 },
  },
  states: { active: { filter: { type: 'none' } } },
  dataLabels: { enabled: false },
  xaxis: {
    categories: PRODUCTS,
    tickAmount: 3,
    labels: {
      formatter: function (v) {
        return '$' + v + 'k'
      },
    },
  },
  // On the source chart the pick is its own column, so a tooltip row for the
  // part would only repeat the total.
  highlightFilter: {
    tooltip: { show: false },
    dataLabels: { value: 'whole' },
  },
  legend: { show: false },
  title: { text: 'Revenue by product', style: { fontSize: '13px' } },
  tooltip: {
    y: {
      formatter: function (v) {
        return '$' + v + 'k'
      },
    },
  },
}

var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()

var options1 = {
  series: quarterSeries(),
  chart: {
    id: 'quarters',
    type: 'bar',
    height: 320,
    toolbar: { show: false },
  },
  colors: ['#2563eb', '#f59e0b'],
  plotOptions: {
    bar: { columnWidth: '70%', borderRadius: 3 },
  },
  dataLabels: { enabled: false },
  stroke: { show: true, width: 1, colors: ['transparent'] },
  xaxis: { categories: QUARTERS },
  yaxis: {
    labels: {
      formatter: function (v) {
        return '$' + v + 'k'
      },
    },
  },
  legend: { position: 'bottom' },
  title: { text: 'Revenue by quarter', style: { fontSize: '13px' } },
  tooltip: {
    shared: true,
    intersect: false,
    y: {
      formatter: function (v) {
        return '$' + v + 'k'
      },
    },
  },
}

var chart1 = new ApexCharts(document.querySelector('#chart2'), options1)
chart1.render()