6

I am trying to set focus to a textInput in a modalDialog in shiny. I am using shinyjs. Following guidance from the shinyjs documentation I came up with the following code which does not do what I expect:

library(shiny)
library(shinyjs)

jscode <- "
shinyjs.refocus = function(e_id) {
  document.getElementById(e_id).focus();
}"

# Define UI for application that draws a histogram
ui <- fluidPage(
    shinyjs::useShinyjs(),
    shinyjs::extendShinyjs(text = jscode, functions = "refocus"),
    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            actionButton("show", "Show Modal")
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
    
    observeEvent(input$show, {
        showModal(ui = modalDialog(title = "my modal dialog",
                                   textInput("text_1", label = "text 1"),
                                   textInput("text_2", label = "text 2"),
                                   js$refocus("text_1")))
    })
    
}

# Run the application 
shinyApp(ui = ui, server = server)

The modalDialog is created with the two textInputs but there is no focus on either input. How can I force input on the first input? I want to avoid that extra click to set the focus on the input. Thanks for any help.

Paul van Oppen
  • 1,443
  • 1
  • 9
  • 18

1 Answers1

7

Your problem is that the moment your js$refocus is called, the modal is not yet set up. If you open the console in the development tools, you will see that there is an error raised.

The better option would be to register an event listener, which fires as soon as the modal is shown. Looking into the docs of modal you can see, that there is a shown.bs.modal event, which is triggered when the modal is loaded.

The event is triggered at the .modal element, which is not existing, but thanks to event bubbling we can listen to this very event at, say, <body> and you can react there.

Enough theory, here's the code (no need for shinyjs):

library(shiny)

## create an event listener at <body> which fires as soon as the modal is shown
## sets the focus to the first text element (adapt if needed)
jscode <- HTML("$('body').on('shown.bs.modal', (x) => 
                   $(x.target).find('input[type=\"text\"]:first').focus())")

# Define UI for application that draws a histogram
ui <- fluidPage(
   # Application title
   titlePanel("Old Faithful Geyser Data"),
   tags$header(tags$script(type = "text/javascript", jscode)),
   # Sidebar with a slider input for number of bins 
   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30),
         actionButton("show", "Show Modal")
      ),
      
      # Show a plot of the generated distribution
      mainPanel(
         plotOutput("distPlot")
      )
   )
)

Simply remove the js$refocus function from your modal and you should be fine. Here's a gif showing the effect:

Set Focus to First Textbox

thothal
  • 16,690
  • 3
  • 36
  • 71
  • Perfect! With the only adjustment that my first element is a `numericInput` of `type = :number'` this worked as expected. Many thanks!! – Paul van Oppen Mar 02 '21 at 02:03