'On click for shiny inputs to get last input clicked doesn't work for selectInput or selectizeInput

I want to trigger a reactive containing the input id of the last input clicked. What I have works fine for some inputs like numericInput and textInput. But it doesn't work for selectInput or selectizeInput. I've tried using a variety of selectors in the JS expression, but none are capturing selectInput or selectizeInput.

Here's a reprex. When you click on either of the first two inputs, the renderText updates, but it doesn't with the last two.

library(shiny)

ui <- fluidPage(
  tags$head(
    tags$script(
      htmlwidgets::JS("$( document ).on('click', '.form-control, .shiny-bound-input, .selectized', function() {
                        Shiny.setInputValue('last_input', this.id);
                      });")
    )
  ),
  
  numericInput("num1", "Numeric", 0),
  textInput("text1", "Text"),
  selectInput("select1", "Select", choices = LETTERS[1:4]),
  selectInput("selectize1", "Selectize", choices = letters[1:4]),
  
  textOutput("textout")
)

server <- function(input, output, session) {
  
  output$textout <- renderText({
    input$last_input
  })
}

shinyApp(ui, server)


Solution 1:[1]

That's because the id is attributed to the select element and you don't click this element.

My first idea was to search for the first parent element having an id:

tags$script(
  HTML("$( document ).on('click', '.form-control, .shiny-bound-input, .selectized', function() {
          var $parentWithId = $(this).closest('*[id]');
          Shiny.setInputValue('last_input', $parentWithId.attr('id'));
        });")
)

but that still doesn't work for selectizeInput.

Maybe you rather want:

tags$script(
  HTML("$(document).on('shiny:inputchanged', function(event) {
          Shiny.setInputValue('last_input', event.name);
        });")
)

which returns the id of the last changed input.


EDIT

As you noted in your comment, this method also sets last_input when the change is caused from the server by an update* function. Here is how to prevent that:

tags$script(
  HTML("$(document)
         .on('shiny:updateinput', function(e) {
            $(e.target).one('shiny:inputchanged', function(ev) {
              ev.stopPropagation();
            });
          })
          .on('shiny:inputchanged', function(e) {
            Shiny.setInputValue('last_input', e.name);
          });")
)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1