Search code examples
javascriptrshiny

Disable button for 5 seconds with different label and change after it


I'm trying to create an intro in my shiny app using the rintrojs package with the introjs function. My goal is to create an intro where the first next button is disabled for 5 seconds and has a different label like 'Wait 5 seconds...'. It currently waits 5 seconds and changes from label but it isn't disabled during the 5 seconds. So you can still click on it. Here is a reproducible example:

library(shiny)
library(rintrojs)

ui <- fluidPage(
  introjsUI(),
  actionButton("start_intro", "Start Intro"),
  h1("Welcome to the Shiny App"),
  p("This is some description of the app.")
)

server <- function(input, output, session) {
  
  steps <- reactive({
    data.frame(
      element = c(NA, "#start_intro"),
      intro = c("Welcome to the app! This first step has a custom button.",
                "This is the start button for the intro tour."),
      position = c("bottom", "bottom")
    )
  })
  
  observeEvent(input$start_intro, {
    introjs(session, 
            options = list(
              steps = steps(),
              nextLabel = "Wait 5 seconds..." 
            ),
            events = list(
              "onbeforechange" = I("
                if (this._currentStep === 0) {
                  var that = this;
                  var nextButton = document.querySelector('.introjs-nextbutton');
                  
                  if (nextButton) {
                    nextButton.innerHTML = 'Wait 5 seconds...';  // Initial label
                    nextButton.disabled = true;                  // Disable the button
                  }
                  
                  setTimeout(function() {
                    that._options.nextLabel = 'next'; // Change label after 5 seconds
                    that._introItems[0].nextLabel = 'next';
                    var nextButton = document.querySelector('.introjs-nextbutton');
                    if (nextButton) {
                      nextButton.innerHTML = 'next';  // Update the label to 'next'
                      nextButton.disabled = false;    // Enable the button after 5 seconds
                    }
                  }, 5000); // 5 seconds delay
                }
              ")
            )
    )
  })
}
 
shinyApp(ui = ui, server = server)

Output:

enter image description here

As you can see it create the 'Wait 5 seconds...' button which is also shown for 5 seconds, but it is not disabled during the 5 seconds. So I was wondering how we can create a button which changes from label after 5 seconds and is disabled during the 5 seconds?


Solution

  • The first problem is that <a> elements don't have a disabled property. Second, nextButton is always NULL because the html isn't initially rendered - pop in a console.log('boop') to see what I mean. Consequently, you need to look for it after rendering is complete.

    Substitute this in after var that = this;:

    function waitForButton() {
      var nextButton = document.querySelector('.introjs-nextbutton');
    
    if (nextButton) {
        nextButton.innerHTML = 'Wait 5 seconds for real now...';
        nextButton.classList.add('introjs-disabled');
        nextButton.style.pointerEvents = 'none'; // Disable click
      } else {
        setTimeout(waitForButton, 100); // Retry after 100ms if button is not found
      }
    }
    waitForButton()
    
    setTimeout(function() {
      that._options.nextLabel = 'next'; // Change label after 5 seconds
      that._introItems[0].nextLabel = 'next';
      var nextButton = document.querySelector('.introjs-nextbutton');
      if (nextButton) {
        nextButton.innerHTML = 'next';  // Update the label to 'next'
        nextButton.classList.remove('introjs-disabled');   // Enable the button after 5 seconds
        nextButton.style.pointerEvents = 'auto'; // Enable click
      }
    }, 5000); // 5 seconds delay