4

I'm working on a project using ggplotly() and am having trouble formatting my hover text. Specifically, I'm using the text aesthetic to format my tooltip, which works great. The problem I'm running into is that I would like to also print the x value at the top of the tooltip a single time, like what occurs when using base {plotly}.

library(tibble) library(ggplot2) library(plotly) # Example dataset df <- tribble( ~TIME, ~CATEGORY, ~VALUE, 1, "Apple", 7, 1, "Banana", 6, 1, "Cat", 3, 2, "Apple", 10, 2, "Banana", 12, 2, "Cat", 0, 3, "Apple", 23, 3, "Banana", 12, 3, "Cat", 3, 4, "Apple", 23, 4, "Banana", 8, 4, "Cat", 3, 5, "Apple", 9, 5, "Banana", 10, 5, "Cat", 3 ) p <- ggplot(df, aes(x = TIME, y = VALUE, color = CATEGORY, text = paste0(CATEGORY, " value = ", VALUE))) + geom_line(group = 1, size = 0.7) # Convert to plotly with unified x tooltip and custom hovertemplate ggplotly(p, tooltip = c("text", "x")) %>% layout(hovermode = "x unified", hoverlabel = list(font = list(family = "Consolas"))) 

Code Above Output Example

I'm aware that I can utilize a hovertemplate to add back in the default plotly behavior, but this effectively undoes the functionality of being able to use the text aesthetic to format the hover, which is really useful, and also still leaves me with questions regarding formatting the X value itself.

ggplotly(p, tooltip = c("text")) %>% layout(hovermode = "x unified", hoverlabel = list(font = list(family = "Consolas"))) #%>% style(hovertemplate = paste0("%{y} Units")) 

Hovertemplate Output Example

So, I'm ultimately wondering if there is a way to include that single X value at the top while still being able to utilize the text aesthetic as the tooltip itself. If that's not possible, how can I use hovertemplate and potential other style inputs to format the tooltip like I am currently doing with the text aesthetic.

2 Answers 2

4

A bit of a hack, but we can add another trace for TIME but set its color to NA:

p <- ggplot(df) + geom_line(aes(x = TIME, y = VALUE, text = paste("TIME:", TIME, "Hours")), color=NA) + geom_line(aes(x = TIME, y = VALUE, color = CATEGORY, text = paste(CATEGORY, "value =", VALUE)), group = 1, size = 0.7) ggplotly(p, tooltip = c("text")) %>% layout(hovermode = "x unified", hoverlabel = list(font = list(family = "Consolas")) 

Sign up to request clarification or add additional context in comments.

Comments

0

Plotly's layout.xaxis has a hoverformat attribute: Source. But setting the hoverformat to something like hoverformat = "Time %{x}" should, but will not work. Newer plotly versions after 3.1.0-rc.1 support unifiedhovertitle but plotly in R uses version 2.11.1, so it won't work. So M's Solution is super elegant.

Since plotly is running JavaScript, you can hook into it using htmlwidgets::onRender() and add a script that tracks mutations within the "hoverlayer". This is the DOM-element in which the whole hover-div including the legendtitletext appears. So each time this titleElement is rerendered we can observe it and instead of just the number add some text like Time: before. You can also add CSS to it.

ggplotly(p, tooltip = c("text", "x")) |> layout( hovermode = "x unified", hoverlabel = list(font = list(family = "Consolas")) ) |> style(hovertemplate = paste0("%{y} Units")) |> htmlwidgets::onRender(" function(el, x) { var hoverlayer = document.querySelector('.hoverlayer'); const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' ) { var titleElement = document.querySelector('.hoverlayer .legendtitletext'); if (titleElement && !titleElement.textContent.startsWith('Time: ')) { // change the legendtitletext (chain with + like shown) titleElement.textContent = 'Time: ' + titleElement.textContent + ' Hours'; titleElement.style.fontWeight='bold'; } } }); }); observer.observe(hoverlayer, {childList: true, subtree: true}); } ") 

res

2 Comments

To the downvoter, please give me feedback on what I can impove
I was shocked to see that you've deleted the answer. Great that you've restored it now. Don't be discouraged by the occasional downvote(s). Sometimes they're justified, they may make you think about your answer and you might improve it. Sometimes they may seem or are unjustified. But if you provide quality answers, eventually (generally speaking) they'll gain the score that they deserve. See this example: stackoverflow.com/posts/79674915/… I remember that it was first downvoted before getting the upvotes (that it may not even deserve). Cheers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.