You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R Shiny应用中让SweetAlert输入框获焦并恢复模态框焦点

Fix Bootstrap Modal Focus Conflict with SweetAlert in Shiny App

I’ve run into this exact Bootstrap modal focus issue before with SweetAlert in Shiny apps—Bootstrap’s built-in enforceFocus method aggressively pulls focus back to the modal, which blocks any inputs in SweetAlert from working properly. Here’s a tested solution that addresses both your requirements:

Step-by-Step Fix

1. Modify the SweetAlert JavaScript Function

We need to temporarily disable Bootstrap’s modal focus enforcement when opening SweetAlert, re-enable it after closing, and ensure the SweetAlert input gets focus automatically. Update your jscode to this:

jscode <- "
shinyjs.swalsavepulse = function(params) {
  var defaultParams = {
    title: null,
    text : null
  };
  params = shinyjs.getParams(params, defaultParams);
  
  // Target the specific Bootstrap modal by its parent ID
  var $modal = $('#modalPulse .modal');
  
  // Temporarily disable Bootstrap's enforceFocus for this modal
  if ($modal.data('bs.modal')) {
    $modal.data('bs.modal').enforceFocus = function() {};
  }
  
  swal({
    title : params.title, 
    text : params.text, 
    type: 'input', 
    inputType : 'text', 
    html: true, 
    showCancelButton : false, 
    showConfirmButton : true, 
    closeOnConfirm: true, 
    confirmButtonColor: '#339FFF', 
    allowOutsideClick: true,
    onOpen: function() {
      // Auto-focus the SweetAlert input after a short delay (ensures it's rendered)
      setTimeout(function() {
        $('.sweet-alert input').focus();
      }, 100);
    }
  },
  function(isConfirm){
    // Restore Bootstrap's original enforceFocus method
    if ($modal.data('bs.modal')) {
      $modal.data('bs.modal').enforceFocus = $.fn.modal.Constructor.prototype.enforceFocus;
      // Return focus to the modal's action button as requested
      $('#message').focus();
    }
    
    if (isConfirm === true) {
      var val1= 1;
      Shiny.onInputChange('pulsefilename', [val1, Math.random()]);
    }
  });
};
"

2. Key Changes Explained

  • Targeted Focus Disable: Instead of overwriting enforceFocus globally (which can break other modals), we only disable it for your specific #modalPulse modal.
  • Auto-Focus SweetAlert Input: The onOpen callback with a short timeout ensures the input gets focus once the SweetAlert is fully rendered.
  • Restore Focus & Behavior: After SweetAlert closes, we revert the modal’s enforceFocus to its original Bootstrap implementation and set focus back to the modal’s "message" button.

3. Full Updated Code

Here’s your complete app with the fix applied:

library(shiny)
library(shinyjs)
library(shinyBS)
library(sweetalertR)
library(shinyjqui)

# ui section --------------------------------------------------------------
jscode <- "
shinyjs.swalsavepulse = function(params) {
  var defaultParams = {
    title: null,
    text : null
  };
  params = shinyjs.getParams(params, defaultParams);
  
  // Target the specific Bootstrap modal by its parent ID
  var $modal = $('#modalPulse .modal');
  
  // Temporarily disable Bootstrap's enforceFocus for this modal
  if ($modal.data('bs.modal')) {
    $modal.data('bs.modal').enforceFocus = function() {};
  }
  
  swal({
    title : params.title, 
    text : params.text, 
    type: 'input', 
    inputType : 'text', 
    html: true, 
    showCancelButton : false, 
    showConfirmButton : true, 
    closeOnConfirm: true, 
    confirmButtonColor: '#339FFF', 
    allowOutsideClick: true,
    onOpen: function() {
      // Auto-focus the SweetAlert input after a short delay (ensures it's rendered)
      setTimeout(function() {
        $('.sweet-alert input').focus();
      }, 100);
    }
  },
  function(isConfirm){
    // Restore Bootstrap's original enforceFocus method
    if ($modal.data('bs.modal')) {
      $modal.data('bs.modal').enforceFocus = $.fn.modal.Constructor.prototype.enforceFocus;
      // Return focus to the modal's action button as requested
      $('#message').focus();
    }
    
    if (isConfirm === true) {
      var val1= 1;
      Shiny.onInputChange('pulsefilename', [val1, Math.random()]);
    }
  });
};
"

ui <- fluidPage(
  shinyjs::useShinyjs(),
  shinyjs::extendShinyjs(text = jscode),
  tags$head(
    tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
    tags$link(rel = "stylesheet", type = "text/css",
              href = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
  ),
  actionButton(inputId = "popup", label = "Popup")
)

# server section ----------------------------------------------------------
server <- function(input, output, session) {
  observeEvent(input$popup, {
    pulseModal <- function(failed = FALSE) {
      modalDialog(
        title = HTML(paste('<span style="color:white; font-size: 16px; font-weight:bold; font-family:sans-serif ">Pulse shape viewer<span>')),
        actionButton(inputId = "message", label = "message"),
        easyClose = TRUE,
        footer = NULL)
    }
    showModal(div(id= "modalPulse", pulseModal() ))
    jqui_draggable(selector = '.modal-content')
  })
  
  observeEvent(input$message, {
    shinyjs::js$swalsavepulse(title = '<span style="color:#339FFF; font-size: 30px">Save pulse shape<span>', text = "Enter file name.")
  })
}

shinyApp(ui, server)

Why Your Previous Attempt Failed

Most online solutions suggest overwriting $.fn.modal.Constructor.prototype.enforceFocus globally, which can cause unexpected behavior with other modals in your app. Our approach targets only your specific modal and disables enforcement temporarily, making it safer and more reliable in Shiny’s context.

内容的提问来源于stack exchange,提问作者Mark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:35:02