Search code examples
javascriptrshinyhtmlwidgetsrhandsontable

How to simplify the JS code in this R shiny code?


The below code I found at How to display a scaled up image in an popup window on mouse hover or on click event on an image displayed in a rhandsontable cell in RShiny? (but wrapped in Shiny) almost gets at what I'm trying to do with widgets in rhandsontable. But I don't know enough JS to modify it to do what I want.

I'd like to create a column with a question mark in each row of the table, and hovering over the question mark causes explanatory text to appear in a bubble. Sort of like what happens when running the below code, but the below code is more complex because it renders a miniature image in each column row, and hovering over the miniature images causes the image to bubble out large. I'd like to replace the miniature images with question marks, and when hovering over the question mark explanatory text pops up in a bubble, different text for each row. As shown in the below image:

enter image description here

Any recommendations for how to achieve this?

Code:

library(magrittr)
library(htmlwidgets)
library(rhandsontable)
library(shiny)

DF = data.frame(
  comments = c(
    "I would rate it ★★★★☆",
    "This is the book about JavaScript"
  ), 
  cover = c(
    "http://ecx.images-amazon.com/images/I/51bRhyVTVGL._SL500_.jpg",
    "http://ecx.images-amazon.com/images/I/51gdVAEfPUL._SL500_.jpg"
  ),
  stringsAsFactors = FALSE
)

ui <- fluidPage(br(),rHandsontableOutput('my_table'))

server <- function(input, output, session) {

  output$my_table <- renderRHandsontable({
  
    rhandsontable::rhandsontable(DF, allowedTags = "<em><b><strong><a><big>", 
        callback = htmlwidgets::JS(
        "$(document).ready(function() {
        $('body').prepend('<div class=\"zoomDiv\"><img src=\"\" class=\"zoomImg\"></div>');
        // onClick function for all plots (img's)
        $('img:not(.zoomImg)').click(function() {
          $('.zoomImg').attr('src', $(this).attr('src')).css({width: '100%'});
          $('.zoomDiv').css({opacity: '1', width: 'auto', border: '1px solid white', borderRadius: '5px', position: 'fixed', top: '50%', left: '50%', marginRight: '-50%', transform: 'translate(-50%, -50%)', boxShadow: '0px 0px 50px #888888', zIndex: '50', overflow: 'auto', maxHeight: '100%'});
        });
        // onClick function for zoomImg
        $('img.zoomImg').click(function() {
          $('.zoomDiv').css({opacity: '0', width: '0%'}); 
        });
      });"
      ),
      width = 800, height = 450, rowHeaders = FALSE) %>%
      hot_cols(colWidths = c(200, 80)) %>%
      hot_col(1, renderer = htmlwidgets::JS("safeHtmlRenderer")) %>%
      hot_col(2, renderer = "
        function(instance, td, row, col, prop, value, cellProperties) {
          var escaped = Handsontable.helper.stringify(value),
            img;
      
          if (escaped.indexOf('http') === 0) {
            img = document.createElement('IMG');
            img.src = value; img.style.width = 'auto'; img.style.height = '80px';
      
            Handsontable.dom.addEvent(img, 'mousedown', function (e){
              e.preventDefault(); // prevent selection quirk
            });
      
            Handsontable.dom.empty(td);
            td.appendChild(img);
          }
          else {
            // render as text
            Handsontable.renderers.TextRenderer.apply(this, arguments);
          }
          return td;
        }")
    })
  }

shinyApp(ui, server)

Solution

  • Here's an option that doesn't totally do what you're asking, but should get you close if you add some css:

    enter image description here

    library(magrittr)
    library(htmlwidgets)
    library(rhandsontable)
    library(shiny)
    
    DF = data.frame(
      comments = c(
        "I would rate it &#x2605;&#x2605;&#x2605;&#x2605;&#x2606;",
        "This is the book about JavaScript"
      ), 
      cover = c(
        "https://as1.ftcdn.net/v2/jpg/03/35/13/14/1000_F_335131435_DrHIQjlOKlu3GCXtpFkIG1v0cGgM9vJC.jpg",
        "https://as1.ftcdn.net/v2/jpg/03/35/13/14/1000_F_335131435_DrHIQjlOKlu3GCXtpFkIG1v0cGgM9vJC.jpg"
      ),
      text = c(
        "descriptive text about img 1",
        "descriptive text about img 2"
      ),
      stringsAsFactors = FALSE
    )
    
    ui <- fluidPage(br(),rHandsontableOutput('my_table'))
    
    server <- function(input, output, session) {
      
      output$my_table <- renderRHandsontable({
        
        rhandsontable::rhandsontable(DF, allowedTags = "<em><b><strong><a><big>",
          width = 800, height = 450, rowHeaders = FALSE) %>%
          hot_cols(colWidths = c(200, 80)) %>%
          hot_col(1, renderer = htmlwidgets::JS("safeHtmlRenderer")) %>%
          hot_col(2, renderer = "
            function(instance, td, row, col, prop, value, cellProperties) {
              var escaped = Handsontable.helper.stringify(value),
                img;
            
    
    
          
              if (escaped.indexOf('http') === 0) {
                img = document.createElement('IMG');
                img.src = value; img.style.width = 'auto'; img.style.height = '80px';
          
                Handsontable.dom.addEvent(img, 'mousedown', function (e){
                  var exists = document.getElementById('test')
                  if (exists === null){
                  var textBlock = instance.params.data[[row]][[2]];
                  var popup = document.createElement('div');
                  popup.className = 'popup';
                  popup.id = 'test';
                  var cancel = document.createElement('div');
                  cancel.className = 'cancel';
                  cancel.innerHTML = '<center><b>close</b></center>';
                  cancel.onclick = function(e) {
                    popup.parentNode.removeChild(popup)
                  }
                  var message = document.createElement('span');
                  message.innerHTML = '<center>' + textBlock + '</center>';
                  popup.appendChild(message);
                  popup.appendChild(cancel);
                  document.body.appendChild(popup);
            
                  }
    
                });
          
                Handsontable.dom.empty(td);
                td.appendChild(img);
                
              }
              else {
                // render as text
                Handsontable.renderers.TextRenderer.apply(this, arguments);
              }
              return td;
            }") %>% 
          hot_cols(colWidths = ifelse(names(DF) != "text", 150, 0.1))
      })
    }
    
    shinyApp(ui, server)