rplotly

How to Display a Data Frame as Hover Text in Plotly for R?


I'm working with Plotly in R and I want to display a small data frame or table in the hover text of my plot.

I tried using HTML-like tags within the hovertemplate, but it seems that Plotly doesn't interpret these tags as expected. Here's a simple example of what I would like to do :

data <- data.frame(
  Category = c("A", "B", "C"),
  Value1 = c(10, 20, 30),
  Value2 = c(40, 50, 60)
)

fig <- plot_ly(data, x = ~Category, y = ~Value1, type = 'bar')

fig <- fig %>% layout(
  hovermode = 'closest',
  hoverlabel = list(
    bgcolor = "white",
    font = list(size = 12)
  )
) %>% 
  add_trace(
    hovertemplate = paste(
      "<b>Category:</b> %{x}<br>",
      "<table>",
      "<tr><td>Value1:</td><td>%{y}</td></tr>",
      "<tr><td>Value2:</td><td>", data$Value2, "</td></tr>",
      "</table>",
      "<extra></extra>"
    )
  )

fig

Solution

  • Pltoly does not allow all HTML elements in the hovertemplate (only br). I can show you two ways to still show tables as hover info. The first one is really easy:

    1 Faking a table using hovertext

    Since your HTML table is so simplistic, you could fake it by using <b> + </b> + spacing

    custom_hover <- paste0(
      "<b>Category:</b> ", data$Category, "<br>",
      "<b>Value1:</b> ", data$Value1, "<br>",
      "<b>Value2:</b> ", data$Value2
    )
    
    plot_ly(data, x = ~Category, y = ~Value1, type = 'bar',
                   hoverinfo = 'text',
                   hovertext = custom_hover) %>%
      layout(
        hovermode = 'closest',
        hoverlabel = list(
          bgcolor = "white",
          font = list(size = 12)
        )
      )
    

    giving

    out

    2 Doing the overly complicated way using HTML / JS

    If you use htmlwidgets::onRender(js_code) with plotly you can execute your own JavaScript inlcuding plotlys's hover / unhover events. This then can be used to show / hide our own tooltip as per my answer here. The data is passed to Javascript using jsonlite::toJSON(data) and used there to display a custom table. In this solution you can go nuts with CSS / HTML and pretty much customize the whole tooltip.

    out1

    library(plotly)
    library(htmlwidgets)
    
    data <- data.frame(
      Category = c("A", "B", "C"),
      Value1 = c(10, 20, 30),
      Value2 = c(40, 50, 60)
    )
    
    js_code <- paste0('
    function(el, x) {
      // Parse data to access in JavaScript
      var data = ', jsonlite::toJSON(data), ';
      
      // Create tooltip element
      var tooltip = document.createElement("div");
      tooltip.style.display = "none";
      tooltip.style.position = "absolute";
      tooltip.style.backgroundColor = "white";
      tooltip.style.border = "1px solid #ddd";
      tooltip.style.color = "black";
      tooltip.style.padding = "8px";
      tooltip.style.borderRadius = "4px";
      tooltip.style.zIndex = "1000";
      tooltip.style.boxShadow = "2px 2px 6px rgba(0,0,0,0.2)";
      tooltip.style.fontFamily = "Arial, sans-serif";
      document.body.appendChild(tooltip);
      
      el.on("plotly_hover", function(e) {
        var pt = e.points[0];
        var category = pt.x;
        var value1 = pt.y; // the hover event gives us the x / y value
        
        // Find matching data row
        var row = data.find(d => d.Category === category);
        var value2 = row ? row.Value2 : "N/A"; // mathc for the value2 using data
        
        
        tooltip.innerHTML = ` 
          <div style="font-weight:bold; margin-bottom:6px">Category: ${category}</div> 
          <table style="width:100%; border-collapse: collapse; border: 1px solid black;">
            <tr>
              <th style="border: 1px solid black; padding: 5px;">Name</th>
              <th style="border: 1px solid black; padding: 5px;">Value</th>
            </tr>
            <tr>
              <td style="padding:3px; font-weight:bold; border: 1px solid black;">Value1:</td>
              <td style="padding:3px; text-align:right; border: 1px solid black;">${value1}</td>
            </tr>
            <tr>
              <td style="padding:3px; font-weight:bold; border: 1px solid black;">Value2:</td>
              <td style="padding:3px; text-align:right; border: 1px solid black;">${value2}</td>
            </tr>
          </table>
        `;
        
        tooltip.style.display = "block";
        tooltip.style.left = (e.event.pageX + 10) + "px";
        tooltip.style.top = (e.event.pageY + 10) + "px";
      });
      
      el.on("plotly_unhover", function(e) {
        tooltip.style.display = "none";
      });
    }
    ')
    
    plot_ly(data, x = ~Category, y = ~Value1, type = 'bar') %>% 
      layout(
        hovermode = 'closest',
        hoverlabel = list(enabled = FALSE)  # Disable default hover label
      ) %>%
      config(displayModeBar = TRUE) %>%
      htmlwidgets::onRender(js_code)