hsilva hsilva - 6 months ago 65
Javascript Question

How to know information about the clicked bar in highchart column r shiny plot

I want to detect some information when the user clicks in a bar of a column highchart plot. I need to know the name and category of the clicked bar, if the name was chosen in the legend or if the background was clicked. The name part is ok, but I cannot discover which is the JS code I have to write to know this type of information.

Many thanks

library("shiny")
library("highcharter")

ui <- shinyUI(
fluidPage(
column(width = 8, highchartOutput("hcontainer", height = "500px")),
column(width = 4, textOutput("text"))
)
)

server <- function(input, output) {

a <- data.frame(b = LETTERS[1:10], c = 11:20, d = 21:30, e = 31:40)

output$hcontainer <- renderHighchart({

myClickFunc <- JS("function(event) {Shiny.onInputChange('hcClicked', this.name);}")

highchart() %>%
hc_xAxis(categories = a$b) %>%
hc_add_serie(name = "c", data = a$c) %>%
hc_add_serie(name = "d", data = a$d) %>%
hc_add_serie(name = "e", data = a$e) %>%
hc_plotOptions(series = list(stacking = FALSE, events = list(click = myClickFunc))) %>%
hc_chart(type = "column")

})

output$text <- renderText({
input$hcClicked
})
}


When I click to disable and immediately after to enable a column, I cannot detect the 2nd event.
How can I detect if a column or the legend was disabled and enabled (or vice versa), without any other different event in the middle?

Answer

I'll probably go more into detail this time about what you can do and what possibilities you have:

A highchart event function always has the form JS("function(event) { ... }"). The JS stands for JavaScript and prevents R from interpreting the code inside. Luckily, JavaScript looks a lot like R, which will make it easier to write statements even if you don't know any JavaScript.

The function inside always has to have an event parameter. From the highcharts documentation, this event has several properties, most important of all, point. Properties in JavaScript can be accessed by a dot ., just like you would use a $ in R. So event.point will be the point you clicked on.

Now, this point also has a lot of properties, of which most are not very useful. But possible values you might want to use are category, color, x and y for respective axis values. So if you'd like to know the y value of the clicked point, event.point.y is the command. Another example: in your case, event.point.x would yield the x value, which in the case of a bar chart is not the category, but the index of the category (counting starts at 0 here). In the previous example (your code snippet), I also used the object this. this stands for the series that has been clicked. This is just a shortcut, because you could also get the series via event.point.series. And the series itself has a lot of properties, like i.e. the name.

Note concerning JavaScript: Lists (arrays) in JavaScript are created with the [ ... ] brackets. I used this in the code below to send multiple values in one command.

That is the first part. Now, you want to communicate the selection to your shiny app. Shiny has inbuilt information channels for this purpose. One is the JavaScript function Shiny.onInputChange. This function has two arguements, the second one is the value you want to send. The first one is the name, under which you want to access the value later on. So the usage is always Shiny.onInputChange('myVar', value). Now this value gets sent to your server and is accessible under input$myVar. There, you have the same rules as for any other input. You can put input$myVar into reactive environments and they will react, whenever something new is sent to input$myVar.

Concerning your direct request: To know what category the clicked value belongs to, can be achieved by changing the initial click function just a bit. (I renamed the functions to suit their functionality.) The explanation above should make it understandable. The second feature, with the legend click, uses another event "slot" that is built into highcharts. The legendItemClick works just like the normal click, but only listens to legend clicks. There are some more event handlers that can be assigned, i.e. mouseOver if you need hover events...

Okay, now here is a sample code to give a working example for what I wrote above.

Best regards

library("shiny")
library("highcharter")

ui <- shinyUI(
  fluidPage(
    column(width = 8, highchartOutput("hcontainer", height = "500px")),
    column(width = 4, textOutput("text"))
  )
)

server <- function(input, output) {      

  a <- data.frame(b = LETTERS[1:10], c = 11:20, d = 21:30, e = 31:40)

  output$hcontainer <- renderHighchart({      

    canvasClickFunction <- JS("function(event) {Shiny.onInputChange('canvasClicked', [this.name, event.point.category]);}")
    legendClickFunction <- JS("function(event) {Shiny.onInputChange('legendClicked', this.name);}")

    highchart() %>% 
      hc_xAxis(categories = a$b) %>% 
      hc_add_serie(name = "c", data = a$c) %>%
      hc_add_serie(name = "d", data = a$d) %>% 
      hc_add_serie(name = "e", data = a$e) %>%
      hc_plotOptions(series = list(stacking = FALSE, events = list(click = canvasClickFunction, legendItemClick = legendClickFunction))) %>%
      hc_chart(type = "column")

  })      

  makeReactiveBinding("outputText")

  observeEvent(input$canvasClicked, {
    outputText <<- paste0("You clicked on series ", input$canvasClicked[1], " and the bar you clicked was from category ", input$canvasClicked[2], ".") 
  })

  observeEvent(input$legendClicked, {
    outputText <<- paste0("You clicked into the legend and selected series ", input$legendClicked, ".")
  })

  output$text <- renderText({
    outputText      
  })
}

shinyApp(ui, server)