Problem/Motivation
When a user navigates away from a page (clicks a link, presses Escape, or uses browser back/forward) before AJAX placeholder content finishes loading, an AJAX error dialog is displayed to the user.
This happens because the browser aborts pending HTTP requests when navigating away, resulting in XMLHttpRequest.status === 0. The module uses Drupal.ajax({ url: endpoint }).execute() without custom error handling, so Drupal's default AJAX error handler shows an error message for what is essentially normal user navigation behavior.
Steps to reproduce
- Add an AJAX placeholder to a page with a callback that takes a few seconds to complete
- Navigate to that page
- Before the placeholder content finishes loading, click any link to navigate away (or press Escape)
- Observe an AJAX error message appearing despite no actual error occurring
Expected behavior
Aborted/cancelled AJAX requests should be silently ignored since they represent normal user navigation, not actual errors.
Actual behavior
An AJAX error dialog is shown to the user with a generic error message, creating a poor user experience.
Current implementation in js/ajax-placeholder.js:
Drupal.behaviors.ajaxPlaceholder = {
attach: function (context) {
once('ajax-placeholder', '.js-ajax-placeholder', context).forEach(function (ajaxPlaceholder) {
let $element = $(ajaxPlaceholder);
let endpoint = Drupal.url('ajax/placeholder/' + $element.data('hash'));
Drupal.ajax({ url: endpoint }).execute(); // No error handling
});
}
};
Proposed resolution
Override the error handler for AJAX placeholder requests to silently ignore aborted requests (status 0).
Proposed changes
Drupal.behaviors.ajaxPlaceholder = {
attach: function (context) {
once('ajax-placeholder', '.js-ajax-placeholder', context).forEach(function (ajaxPlaceholder) {
let $element = $(ajaxPlaceholder);
let endpoint = Drupal.url('ajax/placeholder/' + $element.data('hash'));
let ajaxObject = Drupal.ajax({ url: endpoint });
// Override error handler to ignore aborted requests
var originalError = ajaxObject.error;
ajaxObject.error = function(xmlhttprequest, uri, customMessage) {
// Status 0 indicates the request was aborted (user navigated away, pressed Escape, etc.)
if (xmlhttprequest.status === 0) {
return;
}
originalError.call(this, xmlhttprequest, uri, customMessage);
};
ajaxObject.execute();
});
}
};
Comments