Problem/Motivation
After an AJAX form rebuild (for example after submitting a form with validation errors), the DropzoneJS widget is initialized twice. The old Dropzone instance is not destroyed and remains registered alongside the newly created instance.
Steps to reproduce
- Add a DropzoneJS field to an AJAX-enabled Drupal form.
- Upload a file.
- Submit the form in a way that triggers an AJAX validation error.
- Inspect the Dropzone instances in the browser console:
Dropzone.instances.map(function (dz) {
return {
id: dz.element.id,
files: dz.files.length
};
});Expected result
Only one Dropzone instance exists for the widget element.
Actual result
Multiple Dropzone instances exist after the AJAX rebuild, for example:
[
{
id: "dropzone-widget-id",
files: 1
},
{
id: "dropzone-widget-id--ajax-suffix",
files: 1
}
]The old instance remains registered while a new instance is created for the rebuilt form element.
Impact
The Dropzone state becomes inconsistent. Actions such as removing uploaded files may stop working because multiple Dropzone instances manage the same field lifecycle.
Additional debugging information
Before the AJAX rebuild:
Dropzone.instances.length === 1
After the AJAX rebuild:
Dropzone.instances.length === 2
Both instances contain the uploaded file.
The current cleanup relies on drupalSettings.dropzonejs.instances[selector.attr('id')].instance.destroy(), but after an AJAX rebuild the generated element ID changes, so the previous instance is not reliably destroyed.
Proposed resolution
Ensure that existing Dropzone instances are properly detached and destroyed before a widget is rebuilt during an AJAX form update.
The behavior should guarantee that only one Dropzone instance exists per widget element after Drupal.attachBehaviors() runs.
Possible approaches:
Implement a proper detach() handler for the DropzoneJS behavior and destroy existing instances when Drupal removes/replaces the widget during AJAX processing.
Or, before creating a new Dropzone instance, detect and destroy any existing Dropzone instance associated with the same widget/container element.
Ensure that drupalSettings.dropzonejs.instances remains synchronized with the current DOM element after AJAX rebuilds.
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | 3611403 with patch.png | 279.81 KB | csakiistvan |
| #6 | 3611403 before patch.png | 362.16 KB | csakiistvan |
Issue fork dropzonejs-3611403
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
velmir_taky commentedAdded a
detachhandler to thedropzonejsIntegraionbehavior. Onunloadit destroys the Dropzone instance of each widget being removed (resolved viaDropzone.forElement(), with adrupalSettingsfallback), clears the storeddrupalSettings.dropzonejs.instances[id].instancereference and releases the once() marker.This ensures the old instance is torn down before the rebuilt element is re-attached, so a single instance remains after an AJAX rebuild instead of accumulating duplicates.
Also added
DropzoneJsAjaxRebuildTest(functional JS) covering the scenario: it asserts oneDropzone.instancesentry after an AJAX rebuild — red without the fix, green with it. Verified locally against Chrome via Selenium.Comment #5
csakiistvanComment #6
csakiistvanEnvironment
Prerequisites
dropzonejs_test, which provides the AJAX rebuild test form at/dropzonejs-test/ajax-rebuild(added by MR !35).Steps
detachhandler to the DropzoneJS behavior that destroys the existing Dropzone instance when the element is removed during an AJAX rebuild.ddev drush cr/dropzonejs-test/ajax-rebuild.Dropzone.instances.length(expected: 1).Dropzone.instances.lengthagain.Expected results
Actual results
Before the fix, the DropzoneJS behavior had no detach handler, so after the AJAX rebuild the old instance was left registered while a new one was created on the rebuilt element (which has a different generated ID), leaving
Dropzone.instances.length === 2with broken file removal. After applying the fix, the detach handler destroys the previous instance on unload and clears its stale reference indrupalSettings, so a single instance remains (Dropzone.instances.length === 1) after the rebuild and file removal works as expected.Testing produced with the assistance of an LLM.