0

I would like to query all links with an external script that I integrate into the HTML via a renderUI(). However i get a empty Nodelist []. At which point do i have to include the script?

I tried the following:

R Shiny Script

library(shiny)

ui = navbarPage(
  title = "Test",  
  id = "test", 
  selected = "One", 
  footer = tagList(
    tags$script(src = "custom.js")),
  
  tabPanel(title = "One",
    
           
    div("some links", style = "margin-top: 6rem;"),
    div(uiOutput(outputId = "test_ui")),
    
    
  )
  
)

server = function(input, output, session){
  
  
  output$test_ui  = renderUI({
    
    
    
    tagList(
      div(class = "link-section",
          tags$a("link_one"),
          tags$a("link_two"),
          tags$a("link_three"))
    )
    
  })
  
  
  
  
}
shinyApp(ui = ui, server = server, options = list(port = 3838, host = '0.0.0.0'))

JS Script (located in www folder of app directory)

let links = document.querySelectorAll("a.link-section");
console.log(links);
lucaskr
  • 88
  • 5
  • 1
    The 3rd last lines are in the `custom.js` ? It seems to me that when `custom.js` is loaded Shiny is not yet loaded. Look at `$(document).on('shiny:connected', function(event) {})` in this article https://shiny.rstudio.com/articles/js-events.html – phili_b Feb 16 '23 at 09:26
  • Look at the package `shinyjs` also https://github.com/daattali/shinyjs and https://deanattali.com/shinyjs/extend – phili_b Feb 16 '23 at 09:29
  • `It seems to me that when custom.js is loaded Shiny is not yet loaded` this might be the problem. So my question is where do i have to include the script so that it catches the links. Unfortunately wrapping the two JS lines into `$(document).on('shiny:connected', function(event) {})` leads to an empty Nodelist aswell – lucaskr Feb 16 '23 at 12:11
  • Not tried yet on a R Shiny yet but your css selection is not good, it should be `document.querySelectorAll(".link-section a");` And `shiny:connected` is maybe too late also because you use `renderUI()`. So you have to read this page https://shiny.rstudio.com/articles/communicating-with-js.html and or to use shinyjs: in others terms, load your script after `renderUi()` has done its render. You can read also [github rstudio/shiny issue #2676: Binding Events](https://github.com/rstudio/shiny/issues/2676). – phili_b Feb 16 '23 at 15:23
  • still in the JS learning phase, to what extent do our two querys differ? Thanks for the links, will study them! – lucaskr Feb 16 '23 at 16:47
  • In the HTML generated by your R code `` is inside ` – phili_b Feb 16 '23 at 18:37
  • And when the answer is working and has answered your question, with related links here, you can accept it by clicking the `v` :) FYI `renderUI()` is for dynamic rendering not static rendering : here it adds event complexity for nothing (except obviously if your real application needs dynamic rendering or if it is an exercise or POC.) – phili_b Feb 26 '23 at 11:18
  • Thanks for the answer! I will test it soon, although I think there must be a solution that works without another level of complexity (shinyjs). In this case your are right, renderUI adds complexity for nothing. But in my production app i need it in connection with a textInput(). – lucaskr Feb 27 '23 at 12:11
  • IHMO shinys is not complex, it's a wrapper of concepts linked above. But I've added pure js answer and simplied/fixed my shinyjs answer: for your question without shinyjs it's is actually simpler :) – phili_b Feb 27 '23 at 13:52
  • 1
    Inserting the script into the renderUI with pure Shiny/JS is a good solution. Thanks for staying tuned :-) – lucaskr Feb 28 '23 at 07:08

1 Answers1

0

Shiny with shinyjs

The function name of js function is prefixed by shinyjs. to be known by functions parameter of shinyjs::extendShinyjs() and called in R by js$MySelection().

I've used shiny::onFlushed(function() { js$MySelection() })

"onFlushed registers a function that will be called after Shiny flushes the reactive system.".

custom.js:

shinyjs.MySelection=function() {
  let links = document.querySelectorAll(".link-section a");
  console.log(links);
}

app.R:

library(shiny)
library(shinyjs)


ui =fluidPage(
  div(
    useShinyjs(),
    shinyjs::extendShinyjs(
      script = "custom.js", # without www
      functions = c("MySelection")
    ),
    navbarPage(
      title = "Test",  
      id = "test", 
      selected = "One", 
      
      tabPanel(title = "One",
               
               div("some links", style = "margin-top: 6rem;"),
               div(uiOutput(outputId = "test_ui"))
               
      )
      
    )
    
  )
)

server = function(input, output, session){
  
  output$test_ui  = renderUI({
    tagList(
      div(class = "link-section",
          tags$a("link_one"),
          tags$a("link_two"),
          tags$a("link_three")
          # in case of more delayed events or interactions : 
          # , shinyjs::hidden( textInput("hiddenInput","hiddenlabel"))
      ),
    )
    
  })

  # in case of more delayed events or interactions : 
  # observeEvent(input$hiddenInput,{
  #   js$MySelection()  
  # })
  
  shiny::onFlushed(
    function() {
      js$MySelection()  
    }
  )
  
  
}

shinyApp(ui = ui, server = server)

Pure Shiny without shinyjs

custom.js:

$(document ).on("shiny:sessioninitialized", function(event) {
  MySelection=function() {
    let links = document.querySelectorAll(".link-section a");
    console.log(links);
  }
}) 

app.R:

library(shiny)
ui =fluidPage(
  div(

    tags$head(tags$script(type="text/javascript",src="custom.js")),
    navbarPage(
      title = "Test",  
      id = "test", 
      selected = "One", 
      
      tabPanel(title = "One",
               
               div("some links", style = "margin-top: 6rem;"),
               div(uiOutput(outputId = "test_ui"))
               
      )
      
    )
    
  )
)

server = function(input, output, session){
  
  output$test_ui  = renderUI({
    tagList(
      div(class = "link-section",
          tags$a("link_one"),
          tags$a("link_two"),
          tags$a("link_three")
      ),
      tags$script(type="text/javascript", "MySelection()")
    )
    
  })

  
}

shinyApp(ui = ui, server = server)
phili_b
  • 885
  • 9
  • 27