livewire / livewire/flux

ui-menu crashes when cloneNode creates a detached custom element

Open
#2,824 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Blade
Stars
977
Forks
112
Avg merge
1d 4h
Merged PRs (30d)
21

Description

### Flux version

v2.19.0, still present in v2.20.0

### Livewire version

v4.4.4

### Tailwind version

v4.3.3

### Browser and Operating System

WebKit 26.6 on Linux (Playwright). The same error is reported from Safari 26.6 on iOS 18.7.

### What is the problem?

`ui-menu` throws during custom-element construction when `cloneNode(true)` creates a detached clone:

```
TypeError: null is not an object (evaluating 'this.parentElement.localName')
```

The menu's `boot()` reads `this.parentElement.localName` without checking that a parent exists. Native `cloneNode()` constructs the clone while it is detached, so `parentElement` is `null`. Safari extensions that inspect or copy menu DOM trigger this on otherwise ordinary Flux dropdowns.

The same unguarded code is in every v2.20.0 runtime bundle: `flux/dist/flux.min.js`, `flux/dist/flux-lite.min.js`, `flux-pro/dist/flux.js` and `flux-pro/dist/flux.module.js`.

### Code snippets to replicate the problem

Render an ordinary Flux dropdown:

```blade

Open


One
Two

```

After Flux has initialized, run:

```js
const menu = document.querySelector('ui-dropdown ui-menu')
const clone = menu.cloneNode(true)

console.log(clone.parentElement) // null
```

In WebKit, `cloneNode(true)` returns the detached clone and also reports the uncaught TypeError above. Playwright's `page.on('pageerror', ...)` captures it deterministically.

The relevant code:

```js
if (this.parentElement.localName === "ui-dropdown") {
const dropdown = this.parentElement
// bind dropdown trigger keyboard behavior...
}
```

### Suggested fix

The minimal guard stops the exception:

```diff
-if (this.parentElement.localName === "ui-dropdown") {
+if (this.parentElement?.localName === "ui-dropdown") {
```

On its own, though, a menu constructed detached and inserted into a dropdown later would skip the trigger keyboard wiring. A fuller fix moves the dropdown-parent binding into the element's connected lifecycle and makes it idempotent.

### Screenshots / screen recordings

Not applicable; this is a deterministic page error during native DOM cloning.

### How do you expect it to work?

Cloning a `ui-menu` should not throw. An attached dropdown should keep ArrowDown/ArrowUp behavior, a standalone menu should keep menu keyboard behavior, and a menu constructed detached and later connected to a dropdown should get the same wiring.

### Please confirm

- [x] I have provided easy and step-by-step instructions to reproduce the bug.
- [x] I have provided code samples as text and NOT images.
- [x] I understand my bug report will be closed if I haven't met the criteria above.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start at the ui-menu boot() logic described in the issue and reproduce the failure with cloneNode(true), using Playwright's page.on('pageerror') to observe it. Ensure detached cloning no longer throws, attached dropdowns retain ArrowUp/ArrowDown behavior, standalone menus retain keyboard behavior, and detached menus receive dropdown wiring after connection.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, playwright
Domain
frontend, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.