We are experiencing a persistent issue with the KTSelect component when it's placed inside a KTDrawer that is hidden on the initial page load. The drawer's content is rendered by a Laravel Livewire component.
The Problem:
When the KTDrawer is opened, the KTSelect dropdown does not render correctly. Instead of appearing attached to the select input, its dropdown HTML (<div data-kt-select-dropdown...) is appended to the bottom of the <body> tag. This causes it to be visually detached, incorrectly positioned, and hidden behind the drawer's backdrop overlay.
Environment:
* Laravel
* Laravel Livewire
* Latest version of KeenThemes Metronic Tailwind
Steps to Reproduce:
1. Create a Livewire component that renders a KTDrawer. The drawer should be hidden by default (e.g., using the hidden class).
2. Inside the drawer's HTML, place a standard KTSelect component.
3. Use a button on the page to open the drawer. The opening is triggered by a Livewire event, which then calls the KTDrawer.getInstance(el).show() JavaScript function.
4. When the drawer opens, the KTSelect component is visible, but clicking on it reveals the bug.
Observed Behavior:
The kt-select-dropdown element is created at the end of the <body>, outside of the drawer and its wrapper. It is not positioned correctly relative to the select input.
Expected Behavior:
The kt-select-dropdown element should be rendered within the kt-select-wrapper or positioned correctly by Popper.js, appearing directly below the select input and above the drawer's content and backdrop.
Attempted Solutions
We have tried multiple client-side solutions to fix this, but none have worked. Our core problem seems to be that a global script initializes the KTSelect component on initial page load while its parent drawer is hidden (display: none), leading to a broken state.
Here is a summary of what we've tried:
1. Re-initializing on Drawer Open:
We used a Livewire event (drawer-opened) to trigger JavaScript after the drawer was shown.
* Attempt: Calling KTComponents.init() or new KTSelect(element) after the drawer was visible.
* Result: This had no effect, seemingly because a broken instance of the component already existed from the initial page load.
2. Setting `data-kt-select-dropdown-container`:
* Attempt: We set data-kt-select-dropdown-container to the drawer's ID (e.g., "#my_drawer").
* Result: The dropdown was still appended to the <body>, ignoring the container attribute, likely due to the broken initial state.
3. Destroying the Instance (Destroy and Recreate):
* Attempt: We tried to destroy the existing instance before creating a new one.
1 if (el.instance) {
2 el.instance.destroy(); // FAILED
3 }
4 new KTSelect(el);
* Result: This failed with a JavaScript error: Uncaught TypeError: el.instance.destroy is not a function. This suggests either the
instance object doesn't have a destroy method, or the instance itself was not a valid KTSelect object to begin with.
4. Deferred Initialization (Hiding from Global Scripts):
Our final attempt was to prevent the global initialization script from finding the component in the first place.
* Attempt: We changed the select element's attributes from class="kt-select" and data-kt-select="true" to class="kt-select-deferred"
and data-kt-select-deferred="true". When the drawer opened, our custom script would revert these attributes to their original state
and then call new KTSelect(el).
* Result: This still failed. The dropdown was still created at the bottom of the <body> on initial page load. This is the strongest
evidence that a global script is aggressively finding the component, possibly by just the select tag name or another identifier, and
initializing it regardless of our attempts to "hide" it.
---
Example Code
Blade / Livewire View:
1 <div>
2 <!-- Button to trigger the drawer -->
3 <button type="button" class="btn btn-primary" wire:click="openDrawerAndReinit">
4 Open Drawer
5 </button>
6
7 <!-- The Drawer (hidden by default) -->
8 <div id="example_form_drawer" class="kt-drawer" wire:ignore.self>
9
10 <!-- Drawer Content -->
11 <form>
12 <select class="kt-select" data-kt-select="true" data-kt-select-dropdown-container="#example_form_drawer">
13 <option value="1">Option 1</option>
14 <option value="2">Option 2</option>
15 </select>
16 </form>
17
18 </div>
19 </div>
JavaScript (`app.js`):
This is the code that listens for the Livewire event to open the drawer and initialize components.
1 document.addEventListener("livewire:init", () => {
2 Livewire.on("drawer-opened", ({ drawerId }) => {
3 const drawerElement = document.querySelector(`#${drawerId}`);
4 if (!drawerElement) return;
5
6 const drawerInstance = KTDrawer.getInstance(drawerElement);
7 if (drawerInstance) {
8 drawerInstance.show();
9 }
10
11 // This is where we tried all the re-initialization logic mentioned above.
12 const selectElements = drawerElement.querySelectorAll('[data-kt-select="true"]');
13 selectElements.forEach(el => {
14 // All attempts to re-initialize here have failed.
15 });
16 });
17 });
Our Question:
Is there an official, recommended method for correctly initializing KTSelect (and other components) that exist inside a KTDrawer or
other container that is hidden on initial page load, especially within a dynamic framework like Livewire?
It seems the eager initialization strategy is causing this issue. Any guidance would be greatly appreciated.
Thanks for your time and help.
Hi
The recommended approach is to initialize KTSelect and similar components) only after the container (e.g., KTDrawer) becomes visible or after the DOM is updated. you should use the dropdownContainer: 'body' option, so the dropdown is appended to the body, preventing visibility issues caused by parent containers with overflow or hidden styles.
https://github.com/keenthemes/ktui/issues/17#issuecomment-3027362828
Thanks