Remove library tree event listeners on teardown

Listeners added to the item and collection trees' event bindings
survived unregister() and held ZoteroPane and the views for as long
as the tree objects did. makeClassEventDispatcher() now provides
clearEventListeners(), which the trees call on unregister, so callers
don't need to remove their own listeners. The collection tree focus
listener, a plain DOM listener, is removed in destroy().
This commit is contained in:
Dan Stillman 2026-08-31 15:11:15 -04:00
parent 0e46a5356b
commit 22a1b33a42
4 changed files with 21 additions and 1 deletions

View file

@ -1367,6 +1367,7 @@ var CollectionTree = class CollectionTree extends LibraryTree {
unregister() {
this._uninitialized = true;
Zotero.Notifier.unregisterObserver(this._unregisterID);
this.clearEventListeners();
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1145,6 +1145,7 @@ var ItemTree = class ItemTree extends LibraryTree {
for (let id of this._prefsObserverIDs) {
Zotero.Prefs.unregisterObserver(id);
}
this.clearEventListeners();
this._writeColumnPrefsToFile(true);
}

View file

@ -2374,6 +2374,14 @@ Zotero.Utilities.Internal = {
}
ev.listeners.delete(listener);
};
/**
* Remove all listeners from all events, so that an object being torn down doesn't
* keep its listeners' closures alive
*/
cls.prototype.clearEventListeners = function () {
this._events = null;
};
cls.prototype._waitForEvent = async function (event) {
return new Zotero.Promise((resolve) => {

View file

@ -38,6 +38,8 @@ var ZoteroPane = new function () {
this._listeners = {};
this.__defineGetter__('loaded', function () { return _loaded; });
var _lastSelectedItems = [];
// [element, listener] for the collection tree focus listener added in initCollectionsTree()
var _collectionsTreeFocusListener = null;
var lastFocusedElement = null;
this.lastKeyPress = null;
@ -779,6 +781,11 @@ var ZoteroPane = new function () {
this.serializePersist();
if (_collectionsTreeFocusListener) {
let [elem, listener] = _collectionsTreeFocusListener;
elem.removeEventListener("focus", listener);
_collectionsTreeFocusListener = null;
}
if(this.collectionsView) this.collectionsView.unregister();
if(this.itemsView) this.itemsView.unregister();
if (_syncRemindersObserverID) {
@ -1651,7 +1658,10 @@ var ZoteroPane = new function () {
dragAndDrop: true,
multiSelect: true
});
collectionsTree.firstChild.addEventListener("focus", ZoteroPane.collectionsView.recordCollectionTreeFocus);
let treeElem = collectionsTree.firstChild;
let focusListener = ZoteroPane.collectionsView.recordCollectionTreeFocus;
treeElem.addEventListener("focus", focusListener);
_collectionsTreeFocusListener = [treeElem, focusListener];
}
catch (e) {
Zotero.logError(e);