diff --git a/classes/helpers/FrmFormsHelper.php b/classes/helpers/FrmFormsHelper.php
index b9da117e58..d858d58bc1 100644
--- a/classes/helpers/FrmFormsHelper.php
+++ b/classes/helpers/FrmFormsHelper.php
@@ -532,7 +532,10 @@ public static function get_success_message( $atts ) {
$message = do_shortcode( $message );
$role = $atts['role'] ?? 'status';
- return '
' . $message . '
';
+ // tabindex="-1" makes the success message programmatically focusable without adding it
+ // to the tab order, so js/formidable.js can focus it after an AJAX submit without a
+ // screen reader user having to tab to it.
+ return '' . $message . '
';
}
/**
diff --git a/js/formidable.js b/js/formidable.js
index 41c018a4bd..4b57e6a1b4 100644
--- a/js/formidable.js
+++ b/js/formidable.js
@@ -944,7 +944,9 @@ function frmFrontFormJS() {
function() {
afterFormSubmittedBeforeReplace( object, response );
- replaceContent.replaceWith( response.content );
+ const insertedContent = jQuery( response.content );
+ replaceContent.replaceWith( insertedContent );
+ focusFormMessage( insertedContent );
addUrlParam( response );
@@ -1070,6 +1072,24 @@ function frmFrontFormJS() {
jQuery.ajax( ajaxParams ); // eslint-disable-line no-jquery/no-ajax
}
+ /**
+ * Move focus to the top-level success message after an AJAX submit, so screen reader
+ * users are notified it appeared. Scoped to `insertedContent` (the markup that just
+ * replaced the form) rather than a wider ancestor, so an unrelated `.frm_message`-classed
+ * element elsewhere on the page can never be focused instead.
+ *
+ * @since x.x
+ *
+ * @param {jQuery} insertedContent The markup that just replaced the form.
+ * @return {void}
+ */
+ function focusFormMessage( insertedContent ) {
+ const message = insertedContent.filter( '.frm_message' ).add( insertedContent.find( '.frm_message' ) ).get( 0 );
+ if ( message ) {
+ focusInput( message );
+ }
+ }
+
function afterFormSubmitted( object, response ) {
const tempDiv = document.createElement( 'div' );
tempDiv.innerHTML = response.content;
diff --git a/tests/cypress/e2e/admin.cy.js b/tests/cypress/e2e/admin.cy.js
index 80941a8a93..f5557f80fa 100644
--- a/tests/cypress/e2e/admin.cy.js
+++ b/tests/cypress/e2e/admin.cy.js
@@ -37,7 +37,11 @@ describe( 'Run some basic Formidale tests', function() {
cy.get( '#frm_form_key' ).invoke( 'val' ).then( formKey => {
cy.visit( `/wp-admin/admin-ajax.php?action=frm_forms_preview&form=${ formKey }` );
cy.get( '.frm_button_submit' ).should( 'contain.text', 'Submit' ).click();
- cy.get( '.frm_message' ).should( 'contain.text', 'Your responses were successfully submitted. Thank you!' );
+
+ // Focus should move to the success message so screen reader users are notified it appeared.
+ cy.get( '.frm_message' )
+ .should( 'contain.text', 'Your responses were successfully submitted. Thank you!' )
+ .and( 'be.focused' );
} );
} );
} );