rshinyshinyjsshinymodules

Detect active tab in a shiny module


I have a shiny module with some tabs. The goal is to detect which tab is active during a session and return the id of the tab in the console. I tried using some javascript but it doesn't seem to work. Here I tried to create a reproducible example:

library(shiny)

mod_ui <- function(id) {
  ns <- NS(id)
  
  tagList(
    tabsetPanel(
      id = ns("module_tabs"),
      tabPanel("Tab 1", value = "tab1"),
      tabPanel("Tab 2", value = "tab2"),
      tabPanel("Tab 3", value = "tab3"),
      tabPanel("Tab 4", value = "tab4")
    )
  )
}

mod_server <- function(id) {
  moduleServer(id, function(input, output, session) {

  })
}

ui <- fluidPage(
  
  mod_ui("demo_module"),

)

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

  mod_value <- mod_server("demo_module")
  
  ns <- session$ns
  
  shinyjs::useShinyjs(html = TRUE)
  
  jsCode <- sprintf('
      function getActiveTabByContent() {
        var tabset = $("#%s");
        
        var tabContent = tabset.next(".tab-content");
        var activeContent = tabContent.find("div.tab-pane.active");
        var activeClass = Array.from(activeContent[0].classList)
          .filter(cls => cls.startsWith("tab-content-"))[0];
        
        var tabValue = activeClass ? activeClass.replace("tab-content-", "tab") : null;
        
        if (tabValue) {
          Shiny.setInputValue("%s", tabValue);
          console.log("Active tab (by content class):", tabValue);
        }
      }
      
      $(document).ready(function() {
        getActiveTabByContent();
      });
      
      $("#%s").on("shown.bs.tab", function() {
        getActiveTabByContent();
      });
    ', session$ns("module_tabs"), session$ns("active_tab"), session$ns("module_tabs"))
  
  shinyjs::runjs(jsCode)
  
  active_tab <- reactive({
    req(input$active_tab)
    cat("R console - Active tab:", input$active_tab, "\n")
    input$active_tab
  })
  
  return(active_tab)
}

shinyApp(ui, server)

As you can see it doesn't work. So I was wondering if anyone knows how to detect the active tab in a shiny module by its id? Feel free to share different options.


Solution

  • I'm not sure if this is what you are looking for, but we can simply access the input$`demo_module-module_tabs` :

    library(shiny)
    library(shinyjs)
    
    mod_ui <- function(id) {
      ns <- NS(id)
      tagList(
        tabsetPanel(
          id = ns("module_tabs"),
          tabPanel("Tab 1", value = "tab1"),
          tabPanel("Tab 2", value = "tab2"),
          tabPanel("Tab 3", value = "tab3"),
          tabPanel("Tab 4", value = "tab4")
        )
      )
    }
    
    mod_server <- function(id) {
      moduleServer(id, function(input, output, session) {})
    }
    
    ui <- fluidPage(
      useShinyjs(),
      mod_ui("demo_module"),
      p(),
      tags$b(textOutput("active_tab"))
    )
    
    server <- function(input, output, session) {
      mod_value <- mod_server("demo_module")
      
      ns <- session$ns
      
      output$active_tab <- renderText({
        sprintf("Active tab: %s", input$`demo_module-module_tabs`)
      })
      
      observeEvent(input$`demo_module-module_tabs`, {
        runjs(sprintf("console.log('Active tab (by content class): %s');", input$`demo_module-module_tabs`))
      })
    }
    
    shinyApp(ui, server)