@@ -763,50 +763,68 @@ export default Base.extend({
763763 activateFocusTrap : function ( ) {
764764 var self = this ;
765765 const modal_el = self . $modal [ 0 ] ;
766- var inputsBody = modal_el
767- . querySelector ( `.${ self . options . templateOptions . classBodyName } ` )
768- . querySelectorAll ( `select, input:not([type="hidden"]), textarea, button, a` ) ;
769- var inputsFooter = modal_el
770- . querySelector ( `.${ self . options . templateOptions . classFooterName } ` )
771- . querySelectorAll ( `select, input:not([type="hidden"]), textarea, button, a` ) ;
772- var inputs = [ ] ;
773-
774- for ( const el of [ ...inputsBody , ...inputsFooter ] ) {
775- if ( dom . is_visible ( el ) ) {
776- inputs . push ( el ) ;
766+ const focusable_selector = `select, input:not([type="hidden"]), textarea, button, a` ;
767+
768+ // Re-query visible focusable elements on each Tab press so that
769+ // dynamically loaded content (e.g. AJAX-loaded occurrence lists)
770+ // is always reachable via keyboard.
771+ function getVisibleInputs ( ) {
772+ var bodyEl = modal_el . querySelector (
773+ `.${ self . options . templateOptions . classBodyName } `
774+ ) ;
775+ var footerEl = modal_el . querySelector (
776+ `.${ self . options . templateOptions . classFooterName } `
777+ ) ;
778+ var inputsBody = bodyEl
779+ ? bodyEl . querySelectorAll ( focusable_selector )
780+ : [ ] ;
781+ var inputsFooter = footerEl
782+ ? footerEl . querySelectorAll ( focusable_selector )
783+ : [ ] ;
784+ var inputs = [ ] ;
785+ for ( const el of [ ...inputsBody , ...inputsFooter ] ) {
786+ if ( dom . is_visible ( el ) ) {
787+ inputs . push ( el ) ;
788+ }
789+ }
790+ if ( inputs . length === 0 ) {
791+ inputs = [ ...modal_el . querySelectorAll ( ".modal-title" ) ] ;
777792 }
793+ return inputs ;
778794 }
779795
780- if ( inputs . length === 0 ) {
781- inputs = modal_el . querySelectorAll ( ".modal-title" ) ;
782- }
783- var firstInput = inputs . length !== 0 ? inputs [ 0 ] : null ;
784- var lastInput = inputs . length !== 0 ? inputs [ inputs . length - 1 ] : null ;
785796 var closeInput = modal_el . querySelector ( ".modal-close" ) ;
786797
787- modal_el . addEventListener (
788- "keydown" ,
789- ( e ) => {
790- if ( e . key === "Tab" ) {
791- e . preventDefault ( ) ;
798+ // Remove previous focus trap listener to prevent duplicates
799+ // when activateFocusTrap is called multiple times (e.g. redraw).
800+ if ( self . _focusTrapHandler ) {
801+ modal_el . removeEventListener ( "keydown" , self . _focusTrapHandler ) ;
802+ }
803+ self . _focusTrapHandler = ( e ) => {
804+ if ( e . key === "Tab" ) {
805+ e . preventDefault ( ) ;
792806
793- var target = e . target ;
794- var currentIndex = inputs . indexOf ( target ) ;
795- if ( currentIndex >= 0 && currentIndex < inputs . length ) {
796- var nextIndex = currentIndex + ( e . shiftKey ? - 1 : 1 ) ;
797- if ( nextIndex < 0 || nextIndex >= inputs . length ) {
798- closeInput . focus ( ) ;
799- } else {
800- inputs [ nextIndex ] . focus ( ) ;
801- }
802- } else if ( e . shiftKey && lastInput ) {
803- lastInput . focus ( ) ;
804- } else if ( firstInput ) {
805- firstInput . focus ( ) ;
807+ var inputs = getVisibleInputs ( ) ;
808+ var firstInput = inputs . length !== 0 ? inputs [ 0 ] : null ;
809+ var lastInput = inputs . length !== 0 ? inputs [ inputs . length - 1 ] : null ;
810+ var target = e . target ;
811+ var currentIndex = inputs . indexOf ( target ) ;
812+ if ( currentIndex >= 0 && currentIndex < inputs . length ) {
813+ var nextIndex = currentIndex + ( e . shiftKey ? - 1 : 1 ) ;
814+ if ( nextIndex < 0 || nextIndex >= inputs . length ) {
815+ closeInput . focus ( ) ;
816+ } else {
817+ inputs [ nextIndex ] . focus ( ) ;
806818 }
819+ } else if ( e . shiftKey && lastInput ) {
820+ lastInput . focus ( ) ;
821+ } else if ( firstInput ) {
822+ firstInput . focus ( ) ;
807823 }
808824 }
809- ) ;
825+ } ;
826+ modal_el . addEventListener ( "keydown" , self . _focusTrapHandler ) ;
827+
810828 if ( self . options . backdropOptions . closeOnClick === true ) {
811829 modal_el . addEventListener ( "click" , ( e ) => {
812830 if ( ! e . target . closest ( `.${ self . options . templateOptions . classModal } ` ) ) {
@@ -815,6 +833,8 @@ export default Base.extend({
815833 } ) ;
816834 }
817835
836+ var inputs = getVisibleInputs ( ) ;
837+ var firstInput = inputs . length !== 0 ? inputs [ 0 ] : null ;
818838 if ( firstInput && [ "INPUT" , "SELECT" , "TEXTAREA" ] . includes ( firstInput . nodeName ) ) {
819839 // autofocus first element when opening a modal with a form
820840 firstInput . focus ( ) ;
0 commit comments