Peculiar Behaviour When Simulating Button Press in `textAreaInput`

In a Shiny project, I had included functionality to simulate button presses via the "Enter" key, as demonstrated in @daattali's intermediate Shiny guide. However, I came across some rather peculiar behaviour with textAreaInputs. Specifically, if the "Enter" key is pressed immediately after typing in a textAreaInput, the corresponding reactive value will remain unchanged. However, waiting as little as ~305ms after typing before pressing "Enter" will result in the reactive value being properly updated.

To demonstrate this, I have created a reprex with a slightly modified version of the Shiny app linked to above:

library(shiny)

jscode <- '
$(function() {
  var proxiedElements = $("[data-proxy-click]");
  proxiedElements.each( (idx, el) => {
      var domEl = $(el);
      var proxyEl = $(`#${domEl.data("proxyClick")}`);
      domEl.keypress( e => {
        if (e.which === 13) {
          e.preventDefault();
          proxyEl.click();
        }
      } );
    } );
} );
'

ui <- fluidPage(
  tags$head(tags$script(HTML(jscode))),
  actionButton("btn", "Click me to print the value in the text area"),
  div("Or press Enter when the text area is focused to \"press\" the button"),
  tagAppendAttributes(
    textAreaInput("text", NULL, "foo"),
    `data-proxy-click` = "btn"
  )
)

server <- function(input, output, session) {
  observeEvent(input$btn, {
    cat(input$text, "\n")
  })
}

shinyApp(ui, server)

Shiny applications not supported in static R Markdown documents

Created on 2019-08-12 by the reprex package (v0.3.0)

If the "Enter" key is pressed with the text area focused, foo will be printed to the console. However, if, for example, you then type foobar and immediately press "Enter", foo will be printed to the console again, instead of foobar. If, instead, you wait a small amount of time before pressing "Enter", foobar will be printed to the console, as expected.

As a workaround, I found that forcing the reactive value to update from JavaScript via Shiny.onInputChange() will cause the reactive value to contain the correct value, as demonstrated in this second reprex:

library(shiny)

jscode <- '
$(function() {
  var proxiedElements = $("[data-proxy-click]");
  proxiedElements.each( (idx, el) => {
      var domEl = $(el);
      var proxyEl = $(`#${domEl.data("proxyClick")}`);
      domEl.keypress( e => {
        if (e.which === 13) {
          e.preventDefault();
          // Force update textarea inputs
          var childTextArea = domEl.children( "textarea" ).first();
          if (childTextArea.length) {
            Shiny.onInputChange(childTextArea.attr( "id" ), childTextArea.val());
          }
          proxyEl.click();
        }
      } );
    } );
} );
'

ui <- fluidPage(
  tags$head(tags$script(HTML(jscode))),
  actionButton("btn", "Click me to print the value in the text area"),
  div("Or press Enter when the text area is focused to \"press\" the button"),
  tagAppendAttributes(
    textAreaInput("text", NULL, "foo"),
    `data-proxy-click` = "btn"
  )
)

server <- function(input, output, session) {
  observeEvent(input$btn, {
    cat(input$text, "\n")
  })
}

shinyApp(ui, server)

Shiny applications not supported in static R Markdown documents

Created on 2019-08-12 by the reprex package (v0.3.0)

In this version, input$text will always contain the correct value, no matter how quickly the "Enter" key is pressed after typing in the text area.

Note: This peculiar behaviour occurs with textAreaInputs only; standard textInputs do not exhibit the same behaviour.

I didn't have time to take a closer look as to what might be the underlying cause of this issue, but I thought it worth documenting in the (highly unlikely) event that someone else encounters it.

2 Likes

Update:

Earlier this week, a colleague of mine pointed out that this peculiar behaviour does, in fact, occur with regular textInputs as well. The workaround provided in the original post can still be used; however, a slight modification to the selector is required. That is, the line:

var childTextArea = domEl.children( "textarea" ).first();

must be changed to

var childTextArea = domEl.children( "textarea, input[type=\'text\']" ).first();

(the variable can also be renamed to something more fitting, such as childTextElement, if desired).

1 Like

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.