{"record":{"id":"93647c2a6f9dd8bb","repo":"BookStackApp/BookStack","slug":"can-t-find-options-for-dropdown-menu","errorCode":null,"errorMessage":"Can't find options for dropdown menu","messagePattern":"Can't find options for dropdown menu","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts","lineNumber":119,"sourceCode":"    }\n\n    protected openDropdown(menu: HTMLElement): void {\n        const {toggle, showAside, onOpen} = this.getOptions(menu);\n        menu.hidden = false;\n        positionMenu(menu, toggle, Boolean(showAside), this.isRTL);\n\n        this.openDropdowns.add(menu);\n        menu.addEventListener('mouseover', this.onMenuMouseOver);\n\n        if (onOpen) {\n            onOpen();\n        }\n    }\n\n    protected getOptions(menu: HTMLElement): HandleDropdownParams {\n        const options = this.dropdownOptions.get(menu);\n        if (!options) {\n            throw new Error(`Can't find options for dropdown menu`);\n        }\n\n        return options;\n    }\n\n    /**\n     * Add handling for a new dropdown.\n     */\n     public handle(options: HandleDropdownParams) {\n        const {menu, toggle, showOnHover} = options;\n\n        // Register dropdown\n        this.dropdownOptions.set(menu, options);\n\n        // Configure default events\n        const toggleShowing = (event: MouseEvent) => {\n            menu.hasAttribute('hidden') ? this.openDropdown(menu) : this.closeDropdown(menu);\n        };","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts#L101-L137","documentation":"The dropdown helper keeps a Map of HTMLElement -> HandleDropdownParams tracking registered dropdown menus and their behavior. getOptions(menu) throws when an event fires for a menu element that was never registered in that map. The framework treats unknown dropdown elements as a programming error rather than silently no-oping.","triggerScenarios":"An onOpen/onClose/toggle/showAside handler fires for a dropdown DOM element with no entry in dropdownOptions — e.g. the menu element was created or replaced outside the registration path, the dropdown was unregistered before a late close event, or a second helper instance's handlers query a map they never populated.","commonSituations":"Custom dropdown buttons wired without going through the register flow; DOM elements rebuilt after registration (stale element references); a close event arriving after teardown removed the options.","solutions":["Ensure every dropdown menu element is registered through the helper's setup/add path before its open/close/toggle handlers can fire.","If you rebuild or replace the dropdown DOM, re-register the new element with the helper.","Verify only one dropdown helper instance is in play — handlers must query the same instance that registered the menu.","Unregister/cleanup elements on teardown so handlers and options are removed together."],"exampleFix":"// before\nconst menu = document.createElement('div');\ndropdowns.toggle(menu); // getOptions(menu) throws: never registered\n\n// after\nconst menu = document.createElement('div');\ndropdowns.addDropdown(menu, { /* HandleDropdownParams */ });\ndropdowns.toggle(menu); // safe","handlingStrategy":"validation","validationCode":"// Verify a menu is registered with the helper before invoking its behavior\nfunction isRegisteredDropdown(helper: DropdownHelper, menu: HTMLElement): boolean {\n    return helper.hasOptions(menu); // or track registered elements in a local Set\n}\nif (!isRegisteredDropdown(dropdowns, menu)) {\n    dropdowns.addDropdown(menu, options); // register first\n}\ndropdowns.toggle(menu);","typeGuard":null,"tryCatchPattern":"try {\n    dropdowns.toggle(menu);\n} catch (e) {\n    if (e instanceof Error && e.message.includes(\"Can't find options for dropdown menu\")) {\n        // menu element was stale or never registered: re-register or skip\n    } else { throw e; }\n}","preventionTips":["Always create dropdown behavior through the helper's registration path, never by attaching raw listeners to menu elements.","Re-register menu elements after any DOM rebuild or framework re-render.","Clean up (unregister) menus on teardown so late events don't hit a missing entry.","Avoid instantiating a second dropdown helper instance alongside the editor's own."],"tags":["dropdown","state-management","unregistered-element","wysiwyg"],"backgroundTag":"options-not-registered","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}