diff --git a/planet/js/__tests__/helper.test.js b/planet/js/__tests__/helper.test.js index 500311b9c4..2c430c4fb4 100644 --- a/planet/js/__tests__/helper.test.js +++ b/planet/js/__tests__/helper.test.js @@ -231,6 +231,24 @@ describe("helper.js", () => { expect(document.getElementById("target").style.display).toBe("block"); }); + + it("should remove previous listener when hideOnClickOutside is called again for the same anchor element", () => { + document.body.innerHTML = ` +
Inside
+
Target
+ `; + + const container = document.getElementById("container"); + const spyRemove = jest.spyOn(document, "removeEventListener"); + + hideOnClickOutside([container], "target"); + hideOnClickOutside([container], "target"); + hideOnClickOutside([container], "target"); + + expect(spyRemove).toHaveBeenCalledTimes(2); + + spyRemove.mockRestore(); + }); }); describe("updateCheckboxes", () => { diff --git a/planet/js/helper.js b/planet/js/helper.js index 07c014d2e5..6a68e0a790 100644 --- a/planet/js/helper.js +++ b/planet/js/helper.js @@ -92,19 +92,37 @@ function toggleExpandable(id, c) { el.className = el.className === `${c} open` ? c : `${c} open`; } +const outsideClickListeners = new WeakMap(); + function hideOnClickOutside(eles, other) { + const validEles = (Array.isArray(eles) ? eles : [eles]).filter(Boolean); + if (validEles.length === 0) return; + + const anchor = validEles[0]; + + if (anchor && outsideClickListeners.has(anchor)) { + const prevListener = outsideClickListeners.get(anchor); + document.removeEventListener("click", prevListener); + outsideClickListeners.delete(anchor); + } + // if click not in id, hide const outsideClickListener = event => { - const path = - event.path || - (event.composedPath && event.composedPath()) || - event.composedPath(event.target); + const path = event.path || (event.composedPath && event.composedPath()) || []; let ok = false; - for (let i = 0; i < eles.length; i++) if (path.includes(eles[i])) ok = true; + for (let i = 0; i < validEles.length; i++) { + if (path.includes(validEles[i])) { + ok = true; + break; + } + } if (ok === false) { - document.getElementById(other).style.display = "none"; + const otherEl = document.getElementById(other); + if (otherEl) { + otherEl.style.display = "none"; + } removeClickListener(); } @@ -112,8 +130,15 @@ function hideOnClickOutside(eles, other) { const removeClickListener = () => { document.removeEventListener("click", outsideClickListener); + if (anchor) { + outsideClickListeners.delete(anchor); + } }; + if (anchor) { + outsideClickListeners.set(anchor, outsideClickListener); + } + document.addEventListener("click", outsideClickListener); }