Search code examples
javascriptbar-chartamcharts4

Amchart 4: How to highlight bar/column on click of bar/column in bar plots


I am trying to highlight the bar/column on click of bar/column in Amchart 4. Using the below code, I am getting the value of current clicked bar/column but using the column.isActive property, the bar/column is not getting highlighted.

I found this Amchart official Documentation link, but it is about on click of axis label. I am trying to implement the same but on click of bar/column, not axis label.

This is the code implementation:

am4core.ready(function() {

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end

// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);

// Add data
chart.data = [{
  "country": "USA",
  "visits": 2025
}, {
  "country": "China",
  "visits": 1882
}, {
  "country": "Japan",
  "visits": 1809
}, {
  "country": "Germany",
  "visits": 1322
}, {
  "country": "UK",
  "visits": 1122
}];

// Create axes

var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "country";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 30;

categoryAxis.renderer.labels.template.adapter.add("dy", function(dy, target) {
  if (target.dataItem && target.dataItem.index & 2 == 2) {
    return dy + 25;
  }
  return dy;
});

var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());

// Create series
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueY = "visits";
series.dataFields.categoryX = "country";
series.name = "Visits";
series.columns.template.tooltipText = "{categoryX}: [bold]{valueY}[/]";
series.columns.template.fillOpacity = .8;

var columnTemplate = series.columns.template;
columnTemplate.strokeWidth = 2;
columnTemplate.strokeOpacity = 1;

// Make a panning cursor
chart.cursor = new am4charts.XYCursor();
chart.cursor.behavior = "panXY";
chart.cursor.snapToSeries = series;

chart.events.on("hit", function(ev){
 chart.series.each(function(series) {
     if (series instanceof am4charts.ColumnSeries) {
       series.columns.each(function(column) {
         if (column.dataItem.categories.categoryX == series.tooltipDataItem.dataContext.country) {           
           console.log(column.dataItem.categories.categoryX, series.tooltipDataItem.dataContext.country);
           column.isActive = true;
         } else {
           column.isActive = false;
         }
       })
     }
   })
})
}); // end am4core.ready()

Now, I also found this stackoverflow post link, but that was related to the Amchart version 3 libraray and I am using Amchart 4.

Kindly check this jfiddle implementation.


Solution

  • You do not require the hit listener for this per definition, only if you want to deactivate other columns while activating the clicked one. You merely have to make the columns togglable and define the actual properties in active state.

    You have to make the columns togglable.

    series.columns.template.togglable = true;

    This will toggle the active state from false to true. togglable

    Properties while state is active

    var tActiveState = series.columns.template.states.create("active");
    tActiveState.properties.strokeWidth = 0;
    tActiveState.properties.fill = "crimson";
    

    You have to set the properties of the active state. states

    Deactivate other columns on hit

    series.columns.template.events.on("hit", function(event){
      series.columns.each(function(column){
        if(column !== event.target){
          column.setState("default");
          column.isActive = false
        }
      })
    });
    

    For this we are to use the hit listener again. Event Listeners

    Example based on your code

    am4core.ready(function() {
      // Themes begin
      am4core.useTheme(am4themes_animated);
      // Themes end
    
      // Create chart instance
      var chart = am4core.create("chartdiv", am4charts.XYChart);
    
      // Add data
      chart.data = [{
        "country": "USA",
        "visits": 2025
      }, {
        "country": "China",
        "visits": 1882
      }, {
        "country": "Japan",
        "visits": 1809
      }, {
        "country": "Germany",
        "visits": 1322
      }, {
        "country": "UK",
        "visits": 1122
      }];
    
      // Create axes
    
      var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
      categoryAxis.dataFields.category = "country";
      categoryAxis.renderer.grid.template.location = 0;
      categoryAxis.renderer.minGridDistance = 30;
    
      categoryAxis.renderer.labels.template.adapter.add("dy", function(dy, target) {
        if (target.dataItem && target.dataItem.index & 2 == 2) {
          return dy + 25;
        }
        return dy;
      });
    
      var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    
      // Create series
      var series = chart.series.push(new am4charts.ColumnSeries());
      series.dataFields.valueY = "visits";
      series.dataFields.categoryX = "country";
      series.name = "Visits";
      series.columns.template.tooltipText = "{categoryX}: [bold]{valueY}[/]";
      series.columns.template.fillOpacity = .8;
    
      //REM: Making the columns togglable.
      series.columns.template.togglable = true;
    
      //REM: Setting the properties of the "hover" state
      var tHoverState = series.columns.template.states.create("hover");
      tHoverState.properties.strokeWidth = 1;
      tHoverState.properties.fill = "yellow";
    
      //REM: Setting the properties of the active state "isActive=true"
      var tActiveState = series.columns.template.states.create("active");
      tActiveState.properties.strokeWidth = 0;
      tActiveState.properties.fill = "crimson";
    
      var columnTemplate = series.columns.template;
      columnTemplate.strokeWidth = 2;
      columnTemplate.strokeOpacity = 1;
    
      // Make a panning cursor
      chart.cursor = new am4charts.XYCursor();
      chart.cursor.behavior = "panXY";
      chart.cursor.snapToSeries = series;
    
      //REM: To deactivate other columns on click
      series.columns.template.events.on("hit", function(event){
        series.columns.each(function(column){
          if(column !== event.target){
            column.setState("default");
            column.isActive = false
          }
        })
      });
    
    }); // end am4core.ready()
    <script src="https://www.amcharts.com/lib/4/core.js"></script>
    <script src="https://www.amcharts.com/lib/4/charts.js"></script>
    <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
    
    <div id = 'chartdiv'></div>

    Alternatively you can skip togglable and only use hit

    series.columns.template.events.on("hit", function(event){
      series.columns.each(function(column){
        //REM: Deactivate all non clicked columns
        if(column !== event.target){
          column.setState("default");
          column.isActive = false
        }
        //REM: Toggle the clicked column
        else{
          column.isActive = !column.isActive
        }
      })
    });