使用Livewire+Alpine实现标签页时遇$refs.panel未定义错误求助
问题排查:Alpine $refs.panel 未定义错误
错误信息
Alpine Expression Error: can't access property "contains", $refs.panel is undefined
Expression: "! $refs.panel.contains($event.target) && close()"
Uncaught TypeError: can't access property "contains", $refs.panel is undefined
问题场景
使用Livewire和Alpine构建带标签页的页面,切换标签时触发上述错误,已尝试在App Service Provider中定义Livewire JS加载位置,问题未解决。相关代码如下:
<header> <div class="bg-primaryBlack flex items-center justify-between px-8"> <div> <a href="/"> <x-application-logo class="w-28 h-24" /> </a> </div> <div> <nav> <ul class="flex text-primaryWhite font-semibold"> <li class="mr-5"> <a class="hover:text-gray-400 transition-colors ease-in-out" href="#">Plaques</a> </li> <li class="mr-5"> <a class="hover:text-gray-400 transition-colors ease-in-out" href="#">Pens</a> </li> <li> <a class="hover:text-gray-400 transition-colors ease-in-out" href="#">Lichtenberg</a> </li> </ul> </nav> </div> {{--Parent--}} <div x-data="{ open: false, toggle(){ this.open = this.open ? this.close() : true }, close(focusAfter){ this.open=false focusAfter && focusAfter.focus() } }" @keydown.escape.prevent.stop="close()" x-id="['dropdown-button']" @focusin.window= "! $refs.panel.contains($event.target) && close()" class="relative"> @if(Auth::user()) <div class="flex items-center relative"> {{-- Button --}} <button x-ref="button" @click="toggle()" type="button" :aria-expanded="open" :aria-controls="$id('dropdown-button')" class="text-primaryWhite text-sm block mr-2" href="{{ route('dashboard') }}">{{ auth()->user()->name }}</button> <x-icons.right-caret class="w-5 h-5 fill-primaryWhite" /> <div class="absolute top-0 -left-7"> <x-icons.cart class="w-5 h-5 fill-primaryWhite"/> <a class="absolute -top-3 -left-5 text-xs bg-red-400 px-1.5 rounded-full text-primaryWhite font-bold" href="#">5</a> </div> </div> {{--Panel--}} <div x-ref="panel" x-show="open" @click.outside="close($refs.button)" :id="$id('dropdown-button')" style="display:none" class="absolute top-10 right-3 z-10"> <ul class="bg-secondaryWhite px-4 text-left py-2 space-y-2 rounded uppercase text-sm"> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <x-links.logout/> </li> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <a href="{{ route('dashboard') }}">Dashboard</a> </li> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <a href="#">Cart</a> </li> </ul> </div> @else <a class="font-semibold text-gray-300 mr-4 hover:text-gray-400 transition-colors ease-in-out" href="{{ route('login') }}">Login</a> <a class="font-semibold text-gray-300 hover:text-gray-400 transition-colors ease-in-out" href="{{ route('register') }}">Register</a> @endif </div> </div> </header>
问题原因
绑定了@focusin.window事件的容器,其内部的x-ref="panel"元素仅在用户已登录(Auth::user()为真)时才会渲染。当用户未登录时,页面不存在panel这个引用,标签页切换触发window的focus事件时,Alpine找不到$refs.panel,因此抛出错误。
修复方法
方法1:给focusin事件添加存在性判断
修改@focusin.window的表达式,先确认$refs.panel存在再执行后续逻辑:
@focusin.window=" $refs.panel && ! $refs.panel.contains($event.target) && close()"
方法2:将Alpine逻辑拆分到已登录分支内
把包含下拉菜单逻辑的x-data、事件绑定等代码,移到@if(Auth::user())内部,仅在用户登录时加载这段Alpine逻辑,从根源避免未登录时的引用缺失:
{{-- 移除外层的x-data和相关事件,移到已登录分支里 --}} @if(Auth::user()) <div x-data="{ open: false, toggle(){ this.open = this.open ? this.close() : true }, close(focusAfter){ this.open=false focusAfter && focusAfter.focus() } }" @keydown.escape.prevent.stop="close()" x-id="['dropdown-button']" @focusin.window="! $refs.panel.contains($event.target) && close()" class="relative"> {{-- 按钮和面板代码 --}} <div class="flex items-center relative"> {{-- Button --}} <button x-ref="button" @click="toggle()" type="button" :aria-expanded="open" :aria-controls="$id('dropdown-button')" class="text-primaryWhite text-sm block mr-2" href="{{ route('dashboard') }}">{{ auth()->user()->name }}</button> <x-icons.right-caret class="w-5 h-5 fill-primaryWhite" /> <div class="absolute top-0 -left-7"> <x-icons.cart class="w-5 h-5 fill-primaryWhite"/> <a class="absolute -top-3 -left-5 text-xs bg-red-400 px-1.5 rounded-full text-primaryWhite font-bold" href="#">5</a> </div> </div> {{--Panel--}} <div x-ref="panel" x-show="open" @click.outside="close($refs.button)" :id="$id('dropdown-button')" style="display:none" class="absolute top-10 right-3 z-10"> <ul class="bg-secondaryWhite px-4 text-left py-2 space-y-2 rounded uppercase text-sm"> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <x-links.logout/> </li> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <a href="{{ route('dashboard') }}">Dashboard</a> </li> <li class="hover:bg-primaryGreen hover:text-primaryWhite px-2 rounded"> <a href="#">Cart</a> </li> </ul> </div> </div> @else {{-- 未登录的登录/注册链接 --}} <a class="font-semibold text-gray-300 mr-4 hover:text-gray-400 transition-colors ease-in-out" href="{{ route('login') }}">Login</a> <a class="font-semibold text-gray-300 hover:text-gray-400 transition-colors ease-in-out" href="{{ route('register') }}">Register</a> @endif
内容的提问来源于stack exchange,提问作者dnoble82581
相关产品推荐
相关产品推荐

