如何在R Shiny应用中让SweetAlert输入框获焦并恢复模态框焦点
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
enforceFocusglobally (which can break other modals), we only disable it for your specific#modalPulsemodal. - Auto-Focus SweetAlert Input: The
onOpencallback 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
enforceFocusto 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

