Skip to content
2 changes: 2 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ module.exports = function(grunt) {
[ WORKING_DIR + 'wp-admin/js/theme-plugin-editor.js' ]: [ './src/js/_enqueues/wp/theme-plugin-editor.js' ],
[ WORKING_DIR + 'wp-admin/js/theme.js' ]: [ './src/js/_enqueues/wp/theme.js' ],
[ WORKING_DIR + 'wp-admin/js/updates.js' ]: [ './src/js/_enqueues/wp/updates.js' ],
[ WORKING_DIR + 'wp-admin/js/wp-options-unsaved-changes-confirmation.js' ]: [ './src/js/_enqueues/admin/wp-options-unsaved-changes-confirmation.js' ],
[ WORKING_DIR + 'wp-admin/js/user-profile.js' ]: [ './src/js/_enqueues/admin/user-profile.js' ],
[ WORKING_DIR + 'wp-admin/js/user-suggest.js' ]: [ './src/js/_enqueues/lib/user-suggest.js' ],
[ WORKING_DIR + 'wp-admin/js/widgets/custom-html-widgets.js' ]: [ './src/js/_enqueues/wp/widgets/custom-html.js' ],
Expand Down Expand Up @@ -1192,6 +1193,7 @@ module.exports = function(grunt) {
'src/wp-admin/js/theme-plugin-editor.js': 'src/js/_enqueues/wp/theme-plugin-editor.js',
'src/wp-admin/js/theme.js': 'src/js/_enqueues/wp/theme.js',
'src/wp-admin/js/updates.js': 'src/js/_enqueues/wp/updates.js',
'src/wp-admin/js/wp-options-unsaved-changes-confirmation.js': 'src/js/_enqueues/admin/wp-options-unsaved-changes-confirmation.js',
'src/wp-admin/js/user-profile.js': 'src/js/_enqueues/admin/user-profile.js',
'src/wp-admin/js/user-suggest.js': 'src/js/_enqueues/lib/user-suggest.js',
'src/wp-admin/js/widgets/custom-html-widgets.js': 'src/js/_enqueues/wp/widgets/custom-html.js',
Expand Down
31 changes: 31 additions & 0 deletions src/js/_enqueues/admin/wp-options-unsaved-changes-confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* global wp */
( function () {
var form = document.querySelector( 'form[action="options.php"]' );

if ( ! form ) {
return;
}

var originalFormContent = new URLSearchParams( new FormData( form ) ).toString();
var __ = wp.i18n.__;

function beforeUnloadHandler( event ) {
var currentContent = new URLSearchParams( new FormData( form ) ).toString();
if ( originalFormContent !== currentContent ) {
event.preventDefault();
return __(
'The changes you made will be lost if you navigate away from this page.'
);
Comment on lines +15 to +18
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This beforeunload listener is added via addEventListener(), but the handler only returns a string. For beforeunload, the return value from an event listener is ignored in many browsers; you generally need to set event.returnValue (and typically still call preventDefault()) to reliably trigger the confirmation dialog. Update the handler to assign event.returnValue to the translated message (and return it if needed for older compat).

Suggested change
event.preventDefault();
return __(
'The changes you made will be lost if you navigate away from this page.'
);
var message = __(
'The changes you made will be lost if you navigate away from this page.'
);
event.preventDefault();
event.returnValue = message;
return message;

Copilot uses AI. Check for mistakes.
}
}

// Add the beforeunload listener only once a field is modified, to avoid
// breaking bfcache.
document.addEventListener( 'change', function () {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change listener is attached to document, so any change anywhere on the page (e.g. Screen Options / Help toggles) will register a beforeunload handler and can still disable bfcache even if the settings form itself was never modified. Consider attaching the { once: true } change listener to the settings form (or its inputs) instead, so the beforeunload handler is only registered when the options form changes.

Suggested change
document.addEventListener( 'change', function () {
form.addEventListener( 'change', function () {

Copilot uses AI. Check for mistakes.
window.addEventListener( 'beforeunload', beforeUnloadHandler );
}, { once: true } );

form.addEventListener( 'submit', function () {
window.removeEventListener( 'beforeunload', beforeUnloadHandler );
} );
} )();
2 changes: 2 additions & 0 deletions src/wp-admin/options-discussion.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
'<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>'
);

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

require_once ABSPATH . 'wp-admin/admin-header.php';
?>

Expand Down
2 changes: 2 additions & 0 deletions src/wp-admin/options-general.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
'<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>'
);

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

require_once ABSPATH . 'wp-admin/admin-header.php';
?>

Expand Down
2 changes: 2 additions & 0 deletions src/wp-admin/options-media.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
'<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>'
);

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

require_once ABSPATH . 'wp-admin/admin-header.php';

?>
Expand Down
2 changes: 2 additions & 0 deletions src/wp-admin/options-permalink.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
get_current_screen()->set_help_sidebar( $help_sidebar_content );
unset( $help_sidebar_content );

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

$home_path = get_home_path();
$iis7_permalinks = iis7_supports_permalinks();
$permalink_structure = get_option( 'permalink_structure' );
Expand Down
2 changes: 2 additions & 0 deletions src/wp-admin/options-reading.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
'<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>'
);

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

require_once ABSPATH . 'wp-admin/admin-header.php';
?>

Expand Down
1 change: 1 addition & 0 deletions src/wp-admin/options-writing.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
);

wp_enqueue_script( 'user-profile' );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not enqueue user-profile anymore?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do need to enqueue user-profile as well, re-added it

wp_enqueue_script( 'wp-admin-unsaved-changes-confirmation' );

require_once ABSPATH . 'wp-admin/admin-header.php';
?>
Expand Down
3 changes: 3 additions & 0 deletions src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,9 @@ function wp_default_scripts( $scripts ) {

$scripts->add( 'language-chooser', "/wp-admin/js/language-chooser$suffix.js", array( 'jquery' ), false, 1 );

$scripts->add( 'wp-admin-unsaved-changes-confirmation', "/wp-admin/js/wp-options-unsaved-changes-confirmation$suffix.js", array( 'wp-i18n' ), false, 1 );
$scripts->set_translations( 'wp-admin-unsaved-changes-confirmation' );

$scripts->add( 'user-suggest', "/wp-admin/js/user-suggest$suffix.js", array( 'jquery-ui-autocomplete' ), false, 1 );

$scripts->add( 'admin-bar', "/wp-includes/js/admin-bar$suffix.js", array( 'hoverintent-js' ), false, 1 );
Expand Down
Loading