1

I want to have a tooltip on an axis title of a plotly graphic.

Here is my attempt:

x <- y <- 1:10
dat <- expand.grid(x=x, y=y)
dat <- transform(dat, z=x*y)

jscode <- '
$(document).ready(function(){
  setTimeout(function(){
    $($("#heatmap .g-xtitle text")[0]).attr("title", "hello").attr("data-toggle", "tooltip");
  }, 5000);
})
'

library(shiny)
library(plotly)
shinyApp(
  ui <- fluidPage(
    tags$head(tags$script(jscode)),
    plotlyOutput("heatmap")
  ),
  server = function(input, output){
    output$heatmap <- renderPlotly(plot_ly() %>%
      add_trace(data=dat, x=~x, y=~y, z=~z, type="heatmap") %>%
      layout(
        xaxis = list(title="foo") 
      )
    )
  }
)

The JS code sets as expected the attributes data-toggle and title to the container of the x-axis title, but no tooltip appears. I also tried something like $($("#heatmap .g-xtitle text")[0]).tooltip() in the console, but nothing happens.

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225

2 Answers2

2

I think there are a couple of problems with your code:

  1. Plotly don't like it's SVG code edited. Even though you targeted the right element, Plotly probably removed what you added instantly.
  2. You were missing the code enabling the tooltips app-wise.

I encountered a very similar problem when I wanted to put tooltips on tick labels. It took me quite some time to hack it with custom CSS and JS. The idea is to add a div (rectangle) that perfectly overlays the element you want to enrich with the tooltip. Then you set the CSS opacity of that rectangle to 0 to hide it. But the tooltip on this rectangle is working once you hover it (even though it's invisible). Here is a code visualising the shapes of rectangles.

x <- y <- 1:10
dat <- expand.grid(x = x, y = y)
dat <- transform(dat, z = x * y)

jscode <- "
$(document).ready(function() {
    // Enable tooltips app-wise
    $(\"[data-toggle='tooltip']\").tooltip({container: 'body'}); 
});
"

csscode <- HTML('
    .plot-container {
        position: relative;
    }
    .xaxis-container {
        height: 20px;
        position:absolute;
        bottom: 0;
        left: 40px;
        background: #bfb9b9;
        opacity: 0.3;
    }
    .xaxis-tooltip {
        width: 30px;
        height: 20px;
        background: #000;
        margin:auto;
    }
')

library(shiny)
library(plotly)
shinyApp(
    ui <- fluidPage(
        tags$head(
            tags$script(jscode),
            tags$style(csscode)
        ),
        div(class = 'plot-container',
            plotlyOutput("heatmap"),
            div(
                class = "xaxis-container",
                div(class = "xaxis-tooltip", "data-toggle" = "tooltip", "title" = "hello")
            )
        )
    ),
    server = function(input, output) {
        output$heatmap <- renderPlotly({
            plot_ly() %>%
                add_trace(
                    data = dat,
                    x =  ~ x,
                    y =  ~ y,
                    z =  ~ z,
                    type = "heatmap"
                ) %>%
                layout(xaxis = list(title = "foo")) %>%
                # We can't just center the rectangle relative to the entire plot area.
                # Instead, we need to do it relative to the draglayer.
                # This code sets proper width of .xaxis-container on app start.
                htmlwidgets::onRender(
                    "
                    function(el, x) {
                        var width = $('.draglayer')[0].getBoundingClientRect().width;
                        $('.xaxis-container').css('width', width);
                    }
                    "
                )
        })
    }
)

Change opacity: 0.3; to opacity: 0; in line 21 to have a fully functioning solution.

Mikolaj
  • 1,395
  • 2
  • 13
  • 32
0

Note: Unfortunately, I haven't solved your issue, but hopefully this information is helpful. Good luck!

Background: SVG

  1. plotly creates plots as Scalable Vector Graphics (<svg>)

  2. tooltips for <svg> must be added as a child element named <title>

  3. <title> should be the first child element of its parent

  4. new <svg> elements need to be created in the SVG namespace

SVG Tooltip: Rectangle vs. Plotly x-axis

As a proof of concept, I added a <svg> rectangle to your example. Using JS, I then added a <title> element as the first child of the <rect> element, which produces a tooltip on the rectangle.

svg rectangle tooltip

However, following the same steps for the plotly heatmap does not produce a tooltip on the x-axis title, although a <title> element is added to the x-axis title container.

svg plotly tooltip

Code

library(shiny)
library(plotly)

x <- y <- 1:10
dat <- expand.grid(x=x, y=y)
dat <- transform(dat, z=x*y)

### Create a <svg> rectangle element
testRect <- '<svg width="250" height="75" xmlns="http://www.w3.org/2000/svg">
                <g class="test-rect">
                    <rect x="10" y="10" width="200" height="50" 
                        style="fill:wheat; stroke:blue; stroke-width:1px"/>
                </g>
            </svg>'

### Add a first child title element to the rectangle <svg>
### Hovering over the rectangle displays the tooltip
jscode_testRect <- '$(document).ready(function(){
    setTimeout(function(){
        var titleRect = document.createElementNS("http://www.w3.org/2000/svg", "title");
        titleRect.textContent = "rectangle tooltip";
        var testRect = document.getElementsByClassName("test-rect")[0];
        testRect.insertBefore(titleRect, testRect.childNodes[0]); 
    }, 500);
});'

### Add a first child title element to the SVG
### Hovering over the x-axis title doesn't display tooltip
jscode_xaxisPlotly <- '$(document).ready(function(){
    setTimeout(function(){
        var titleAxis = document.createElementNS("http://www.w3.org/2000/svg", "title");
        titleAxis.textContent = "x-axis tooltip";
        var gXtitle = document.getElementsByClassName("g-xtitle")[0];
        gXtitle.insertBefore(titleAxis, gXtitle.childNodes[0]); 
    }, 500);
});'

shinyApp(
    ui <- fluidPage(
        tags$head(tags$script(HTML(jscode_testRect))),
        tags$head(tags$script(HTML(jscode_xaxisPlotly))),
        h4("<svg> rectangle - tooltip is functional"),
        tags$div(HTML(testRect)),
        h4("plotly heatmap - tooltip does not work"),
        plotlyOutput("heatmap"),
        br(),
        br()
    ),
    server = function(input, output){
        output$heatmap <- renderPlotly(plot_ly() %>%
                                           add_trace(data=dat, x=~x, y=~y, z=~z, type="heatmap") %>%
                                           layout(
                                               xaxis = list(title="foo"),
                                               title = "Title"
                                           )
        )
    }
)
Hallie Swan
  • 2,714
  • 1
  • 15
  • 23