实现点击外部或关闭按钮关闭颜色选择器对话框
解决方案:为Blazor颜色选择器添加两种关闭方式
一、添加关闭按钮
首先修改自定义的ColorPicker组件,增加关闭按钮,点击时触发Closed回调并传递当前颜色,确保未选择新颜色时保留原有值。
修改ColorPicker组件代码
@if (IsOpened) { <div class="color-picker-container"> <!-- 原有颜色选择器UI内容 --> <div class="color-picker-actions mt-2"> <button class="btn btn-sm btn-secondary" @onclick="() => Closed.InvokeAsync(MyColor)"> 关闭 </button> </div> </div> } @code { [Parameter, EditorRequired] public bool IsOpened { get; set; } [Parameter, EditorRequired] public string MyColor { get; set; } [Parameter, EditorRequired] public EventCallback<string> Closed { get; set; } // 组件原有其他逻辑... }
点击关闭按钮时,会调用Closed回调并传入当前颜色,你的OnClose方法只会关闭弹窗,不会修改已选颜色。
二、实现点击外部区域关闭弹窗
提供两种可靠实现方式,可根据场景选择:
方式1:利用Blazor事件冒泡与阻止冒泡
通过页面根容器的点击事件关闭所有弹窗,同时在弹窗内部阻止事件冒泡,避免点击弹窗内部触发关闭。
修改页面代码
<!-- 根容器:点击外部区域时关闭所有弹窗 --> <div @onclick="CloseAllColorPickers" class="container mt-3"> @foreach(var picker in _pickers) { <button class="btn btn-primary me-2" @onclick="() => this.OnOpen(picker)" @onclick:stopPropagation> <!-- 阻止按钮点击事件冒泡到根容器 --> <div style="background-color:@picker.Color" class="buttonColor"> </div> @picker.Title </button> @if (picker.IsOpen) { <!-- 弹窗容器:阻止内部点击事件冒泡到根容器 --> <div @onclick:stopPropagation class="position-absolute p-2 border rounded bg-white"> <ColorPicker IsOpened="picker.IsOpen" MyColor="@picker.Color" Closed="(value) => this.OnClose(picker, value)" /> </div> } } </div> @code{ private List<ColorPickerData> _pickers = new(); protected override Task OnInitializedAsync() { _pickers.Add(new() { Title="Picker 1" }); _pickers.Add(new() { Title="Picker 2" }); _pickers.Add(new() { Title = "Picker 3" }); return Task.CompletedTask; } private Task OnClose(ColorPickerData data, string value) { data.IsOpen = false; data.Color = value; return Task.CompletedTask; } private Task OnOpen(ColorPickerData data) { // 打开当前弹窗前关闭其他弹窗(可选,避免多弹窗同时显示) foreach(var p in _pickers) { p.IsOpen = false; } data.IsOpen = true; return Task.CompletedTask; } // 关闭所有颜色选择器弹窗 private void CloseAllColorPickers() { foreach(var picker in _pickers) { picker.IsOpen = false; } } public class ColorPickerData { public string Title { get; set; } = "Color Picker"; public bool IsOpen { get; set; } public string Color { get; set; } = "#000000"; } }
方式2:使用JS互操作监听外部点击
如果需要精确控制仅关闭当前打开的弹窗,可通过JS监听document点击事件,判断点击位置是否在弹窗外部。
步骤1:添加JS代码
在wwwroot/js/site.js中添加以下代码:
let clickOutsideHandler = null; // 添加外部点击监听 window.addClickOutsideListener = function(dotNetRef, targetElementId) { // 移除之前的监听,避免重复绑定 if (clickOutsideHandler) { document.removeEventListener('click', clickOutsideHandler); } clickOutsideHandler = (event) => { const targetElement = document.getElementById(targetElementId); // 点击目标不在弹窗内时,通知Blazor关闭弹窗 if (targetElement && !targetElement.contains(event.target)) { dotNetRef.invokeMethodAsync('CloseCurrentColorPicker'); } }; document.addEventListener('click', clickOutsideHandler); }; // 移除外部点击监听 window.removeClickOutsideListener = function() { if (clickOutsideHandler) { document.removeEventListener('click', clickOutsideHandler); clickOutsideHandler = null; } };
步骤2:修改页面代码
@implements IDisposable @inject IJSRuntime JSRuntime <div class="container mt-3"> @foreach(var picker in _pickers) { <button class="btn btn-primary me-2" @onclick="() => this.OnOpen(picker)"> <div style="background-color:@picker.Color" class="buttonColor"> </div> @picker.Title </button> <ColorPicker IsOpened="picker.IsOpen" MyColor="@picker.Color" Closed="(value) => this.OnClose(picker, value)" ElementId="@($"color-picker-{picker.GetHashCode()}")" /> } </div> @code{ private List<ColorPickerData> _pickers = new(); private DotNetObjectReference<YourComponentName> _dotNetRef; // 替换为你的组件类名 protected override void OnInitialized() { _dotNetRef = DotNetObjectReference.Create(this); } protected override async Task OnAfterRenderAsync(bool firstRender) { foreach (var picker in _pickers) { if (picker.IsOpen) { // 为当前打开的弹窗添加外部点击监听 await JSRuntime.InvokeVoidAsync("addClickOutsideListener", _dotNetRef, $"color-picker-{picker.GetHashCode()}"); } else { // 弹窗关闭时移除监听 await JSRuntime.InvokeVoidAsync("removeClickOutsideListener"); } } await base.OnAfterRenderAsync(firstRender); } protected override Task OnInitializedAsync() { _pickers.Add(new() { Title="Picker 1" }); _pickers.Add(new() { Title="Picker 2" }); _pickers.Add(new() { Title = "Picker 3" }); return Task.CompletedTask; } private Task OnClose(ColorPickerData data, string value) { data.IsOpen = false; data.Color = value; // 移除监听 JSRuntime.InvokeVoidAsync("removeClickOutsideListener"); return Task.CompletedTask; } private Task OnOpen(ColorPickerData data) { // 关闭其他弹窗 foreach(var p in _pickers) { p.IsOpen = false; } data.IsOpen = true; return Task.CompletedTask; } // JS调用的方法:关闭当前弹窗 [JSInvokable] public void CloseCurrentColorPicker() { foreach(var picker in _pickers) { picker.IsOpen = false; } StateHasChanged(); } public void Dispose() { _dotNetRef.Dispose(); // 页面销毁时移除监听 JSRuntime.InvokeVoidAsync("removeClickOutsideListener"); } public class ColorPickerData { public string Title { get; set; } = "Color Picker"; public bool IsOpen { get; set; } public string Color { get; set; } = "#000000"; } }
步骤3:修改ColorPicker组件
添加ElementId参数,用于标识弹窗容器:
@if (IsOpened) { <div id="@ElementId" class="color-picker-container p-2 border rounded bg-white"> <!-- 原有颜色选择UI --> <button class="btn btn-sm btn-secondary mt-2" @onclick="() => Closed.InvokeAsync(MyColor)"> 关闭 </button> </div> } @code { [Parameter, EditorRequired] public bool IsOpened { get; set; } [Parameter, EditorRequired] public string MyColor { get; set; } [Parameter, EditorRequired] public EventCallback<string> Closed { get; set; } [Parameter, EditorRequired] public string ElementId { get; set; } // 组件原有逻辑... }
为什么@onfocusout不生效?
@onfocusout依赖元素的焦点状态,但弹窗容器通常不是可聚焦元素(除非设置tabindex),且当焦点在弹窗内部子元素时,父容器的focusout会被触发,导致误关闭,因此不适用于弹窗外部点击关闭场景。
内容的提问来源于stack exchange,提问作者T335
相关产品推荐
相关产品推荐

