user63230
user63230

Reputation: 4698

plotly including multiple hyperlinks in text

Is there a way to hover over data in a plotly graph and then be able to click on a choice of hyperlinks within the text?

There are a number of questions (e.g., here, here) that allow the user to click on a point and that brings you to the url associated with that point but in those solutions it is restricted to only one url. For example:

library(ggplot2)
library(plotly)
library(htmlwidgets)
mydata <- data.frame( xx = c(1, 2),  yy = c(3, 4),
  website = c("https://www.google.com",
              "https://www.r-project.org/"),
  link = c(
    "https://www.google.com",
    "https://www.r-project.org/"))


g <- ggplot(mydata, aes(x = xx, y = yy, 
                        text = paste0("xx: ", xx, "\n",
                                      "website link: ", website),
                        customdata = link)) +
  geom_point()
g
p <- ggplotly(g, tooltip = c("text"))
p
onRender(
  p, "
  function(el) {
    el.on('plotly_click', function(d) {
      var url = d.points[0].customdata;
      window.open(url);
    });
  }
"
)

You can then click on the second point and it will bring you to https://www.r-project.org/ : enter image description here

What I want is to be able to choice between two or more links (i.e. click on a hyperlink within the textbox):

mydata <- data.frame( xx = c(1, 2),  yy = c(3, 4),
                      website = c("https://www.google.com",
                                  "https://www.r-project.org/),
                      website2 = c(" https://www.reddit.com/", 
                                   "http://stackoverflow.com/"),
                      link = c(
                        "https://www.google.com, https://www.reddit.com/",
                        "https://www.r-project.org/, http://stackoverflow.com/"))


g <- ggplot(mydata, aes(x = xx, y = yy, 
                        text = paste0("xx: ", xx, "\n",
                                      "website link: ", website, "\n",
                                      "Second website: ", website2),
                        customdata = link)) +
  geom_point()
g
p <- ggplotly(g, tooltip = c("text"))
p

enter image description here

I sense this cannot be achieved with text or tooltip from plotly but perhaps there is a different workaround using e.g. javascript (which I am not familiar with).

Any ideas?

Thanks

Upvotes: 2

Views: 159

Answers (2)

St&#233;phane Laurent
St&#233;phane Laurent

Reputation: 84599

Here is a way without Shiny, using the jqueryUI library:

library(plotly)
library(htmlwidgets)
library(htmltools)

dep <- htmlDependency(
  name = "jquery-ui",
  version = "1.13.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2"),
  script = "jquery-ui.min.js",
  stylesheet = "themes/base/jquery-ui.min.css"
)


mydata <- data.frame(
  xx = c(1, 2),  
  yy = c(3, 4),
  website = c("https://www.google.com/",
              "https://www.r-project.org/"),
  website2 = c("https://www.reddit.com/", 
               "http://stackoverflow.com/"),
  link = I(list(
    list("https://www.google.com", "https://www.reddit.com/"),
    list("https://www.r-project.org/", "http://stackoverflow.com/")
  ))
)

g <- ggplot(
  mydata, 
  aes(
    x = xx, 
    y = yy, 
    text = paste0(
      "xx: ", xx, "\n",
      "website link: ", website, "\n",
      "Second website: ", website2
    ),
    customdata = link
  )) +
  geom_point()
p <- ggplotly(g, tooltip = c("text")) %>% onRender(
  "function(el) {
    el.on('plotly_click', function(d) {
      var urls = d.points[0].customdata;
      $div = $('<div><p><a href=\"' + urls[0] + '\">First link</a></p><p><a href=\"' + urls[1] + '\">Second link</a></p></div>');
      $div.dialog({
        autoOpen: false,
        show: {effect: 'blind', duration: 1000},
        hide: {effect: 'explode', duration: 1000}
      });
      $div.dialog('open');
    });
  }"
)
deps <- c(p$dependencies, list(dep))
p$dependencies <- deps

p

Using the SweetAlert2 library:

library(shiny)
library(plotly)
library(htmlwidgets)
library(htmltools)

dep <- htmlDependency(
  name = "sweetalert2",
  version = "11.6.15",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.6.15"),
  script = "sweetalert2.all.min.js"
)


mydata <- data.frame(
  xx = c(1, 2),  
  yy = c(3, 4),
  website = c("https://www.google.com/",
              "https://www.r-project.org/"),
  website2 = c("https://www.reddit.com/", 
               "http://stackoverflow.com/"),
  link = I(list(
    list("https://www.google.com", "https://www.reddit.com/"),
    list("https://www.r-project.org/", "http://stackoverflow.com/")
  ))
)

g <- ggplot(
  mydata, 
  aes(
    x = xx, 
    y = yy, 
    text = paste0(
      "xx: ", xx, "\n",
      "website link: ", website, "\n",
      "Second website: ", website2
    ),
    customdata = link
  )) +
  geom_point()
p <- ggplotly(g, tooltip = c("text")) %>% onRender(
  "function(el) {
    el.on('plotly_click', function(d) {
      var urls = d.points[0].customdata;
      var html = '<div><p>' + 
        '<a href=\"' + urls[0] + '\" target=\"_blank\">First link</a>' +
        '</p><p>' + 
        '<a href=\"' + urls[1] + '\" target=\"_blank\">Second link</a>' + 
        '</p></div>';
      Swal.fire({
        title: 'Links',
        html: html
      });
    });
  }"
)
deps <- c(p$dependencies, list(dep))
p$dependencies <- deps

p

enter image description here


More stylish:

library(shiny)
library(plotly)
library(htmlwidgets)
library(htmltools)

dep <- htmlDependency(
  name = "sweetalert2",
  version = "11.6.15",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.6.15"),
  script = "sweetalert2.all.min.js"
)


mydata <- data.frame(
  xx = c(1, 2),  
  yy = c(3, 4),
  link = I(list(
    list(
      list(title = "Google", url = "https://www.google.com"), 
      list(title = "Reddit", url = "https://www.reddit.com/")
    ),
    list(
      list(title = "R project", url = "https://www.r-project.org/"), 
      list(title = "StackOverflow", url = "http://stackoverflow.com/")
    )
  ))
)

g <- ggplot(
  mydata, 
  aes(
    x = xx, 
    y = yy, 
    text = paste0("xx: ", xx),
    customdata = link
  )) +
  geom_point()
p <- ggplotly(g, tooltip = c("text")) %>% onRender(
  "function(el) {
    el.on('plotly_click', function(d) {
      var urls = d.points[0].customdata;
      var html = '<hr/><div><p>' + 
        '<a href=\"' + urls[0].url + '\" target=\"_blank\">' + 
          urls[0].title + 
        '</a>' +
        '</p><p>' + 
        '<a href=\"' + urls[1].url + '\" target=\"_blank\">' + 
          urls[1].title +
        '</a>' + 
        '</p></div>';
      Swal.fire({
        title: '<strong>Links</strong>',
        html: html
      });
    });
  }"
)
deps <- c(p$dependencies, list(dep))
p$dependencies <- deps

p

enter image description here


You can also animate the sweet alerts with the Animate.css library:

library(shiny)
library(plotly)
library(htmlwidgets)
library(htmltools)

dep_sweetalert2 <- htmlDependency(
  name = "sweetalert2",
  version = "11.6.15",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.6.15"),
  script = "sweetalert2.all.min.js"
)
dep_animate.css <- htmlDependency(
  name = "animate.css",
  version = "4.1.1",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1"),
  stylesheet = "animate.min.css"
)


mydata <- data.frame(
  xx = c(1, 2),  
  yy = c(3, 4),
  link = I(list(
    list(
      list(title = "Google", url = "https://www.google.com"), 
      list(title = "Reddit", url = "https://www.reddit.com/")
    ),
    list(
      list(title = "R project", url = "https://www.r-project.org/"), 
      list(title = "StackOverflow", url = "http://stackoverflow.com/")
    )
  ))
)

g <- ggplot(
  mydata, 
  aes(
    x = xx, 
    y = yy, 
    text = paste0("xx: ", xx),
    customdata = link
  )) +
  geom_point()
p <- ggplotly(g, tooltip = c("text")) %>% onRender(
  "function(el) {
    el.on('plotly_click', function(d) {
      var urls = d.points[0].customdata;
      var html = '<hr/><div><p>' + 
        '<a href=\"' + urls[0].url + '\" target=\"_blank\">' + 
          urls[0].title + 
        '</a>' +
        '</p><p>' + 
        '<a href=\"' + urls[1].url + '\" target=\"_blank\">' + 
          urls[1].title +
        '</a>' + 
        '</p></div>';
      Swal.fire({
        title: '<strong>Links</strong>',
        html: html,
        showClass: {popup: 'animate__animated animate__rollIn'},
        hideClass: {popup: 'animate__animated animate__rollOut'}
      });
    });
  }"
)
deps <- c(p$dependencies, list(dep_sweetalert2, dep_animate.css))
p$dependencies <- deps

p

enter image description here

Upvotes: 1

St&#233;phane Laurent
St&#233;phane Laurent

Reputation: 84599

Here is a way with Shiny:

library(plotly)
library(htmlwidgets)
library(shiny)

mydata <- data.frame(
  xx = c(1, 2),  
  yy = c(3, 4),
  website = c("https://www.google.com/",
              "https://www.r-project.org/"),
  website2 = c("https://www.reddit.com/", 
               "http://stackoverflow.com/"),
  link = I(list(
    list("https://www.google.com", "https://www.reddit.com/"),
    list("https://www.r-project.org/", "http://stackoverflow.com/")
  ))
)

g <- ggplot(
  mydata, 
  aes(
    x = xx, 
    y = yy, 
    text = paste0(
      "xx: ", xx, "\n",
      "website link: ", website, "\n",
      "Second website: ", website2
    ),
    customdata = link
  )) +
  geom_point()
p <- ggplotly(g, tooltip = c("text")) %>% onRender(
  "function(el) {
    el.on('plotly_click', function(d) {
      var urls = d.points[0].customdata;
      Shiny.setInputValue('urls', urls);
    });
  }"
)


ui <- fluidPage(
  plotlyOutput("plotly")
)

server <- function(input, output, session) {
  
  output[["plotly"]] <- renderPlotly({
    p
  })
  
  observeEvent(input[["urls"]], {
    url1 <- input[["urls"]][1]
    url2 <- input[["urls"]][2]
    showModal(modalDialog(
      tags$div(
        tags$a(href = url1, "First link"),
        tags$br(),
        tags$a(href = url2, "Second link")
      )
    ))
  })
  
}

shinyApp(ui, server)

Upvotes: 1

Related Questions