Blazor WebAssembly按钮适配桌面onclick与移动端ontouchstart防重复触发求助
解决方案
方案1:使用Pointer事件统一处理(最优)
W3C标准的Pointer事件已经统一封装了鼠标、触摸、手写笔等所有输入设备的交互事件,不需要分开监听touch和click事件,从根源避免重复触发问题,也不需要处理preventDefault相关的passive监听器限制。
实现代码
如果要求所有设备都在按下时立即响应,直接监听@onpointerdown即可:
<button @onpointerdown="e => ToggleFlag(flag)"> @flag.DisplayValue </button>
如果需要保持桌面端鼠标点击(抬手后触发)的交互习惯,仅移动端触摸立即响应,可以通过指针类型判断加状态锁实现:
<button @onpointerdown="HandlePointerDown" @onclick="HandleClick" style="touch-action: none !important;"> @flag.DisplayValue </button> @code { // 触摸操作触发锁,避免后续300ms延迟的click事件重复执行 private bool _touchTriggered; private void HandlePointerDown(PointerEventArgs e) { if (e.PointerType == "touch") { _touchTriggered = true; ToggleFlag(flag); // 300ms后自动重置锁,覆盖移动端默认的click事件延迟间隔 _ = Task.Delay(300).ContinueWith(_ => _touchTriggered = false); } } private void HandleClick() { if (!_touchTriggered) { ToggleFlag(flag); } } }
补充:给按钮添加
touch-action: none !important;样式可以直接禁止触摸时的页面滚动等默认行为,不需要额外调用preventDefault即可生效。
方案2:状态锁兼容原有逻辑
如果不想改动原有事件监听逻辑,可以直接加短周期的执行锁避免重复调用:
<button @onclick="e => HandleClick(flag)" @ontouchstart="e => HandleTouchStart(flag)" style="touch-action: none !important;"> @flag.DisplayValue </button> @code { private bool _isExecuting; private async void HandleTouchStart(FlagType flag) { if (_isExecuting) return; _isExecuting = true; ToggleFlag(flag); await Task.Delay(300); _isExecuting = false; } private async void HandleClick(FlagType flag) { if (_isExecuting) return; _isExecuting = true; ToggleFlag(flag); await Task.Delay(300); _isExecuting = false; } }
方案3:JS互操作注册非Passive触摸监听器
如果必须使用preventDefault阻止默认行为,可以通过JS互操作手动注册非passive的触摸事件监听器,绕开Blazor默认的passive参数限制:
- 在
wwwroot/index.html中添加JS注册逻辑:
window.registerTouchStartWithoutPassive = (element, dotNetRef, callbackMethodName) => { element.addEventListener('touchstart', (e) => { e.preventDefault(); dotNetRef.invokeMethodAsync(callbackMethodName); }, { passive: false }); };
- 组件中调用JS注册事件:
<button @ref="_buttonRef" @onclick="e => ToggleFlag(flag)"> @flag.DisplayValue </button> @code { private ElementReference _buttonRef; private DotNetObjectReference<YourComponentName>? _dotNetRef; [Inject] private IJSRuntime JSRuntime { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _dotNetRef = DotNetObjectReference.Create(this); await JSRuntime.InvokeVoidAsync("registerTouchStartWithoutPassive", _buttonRef, _dotNetRef, nameof(ToggleFlag)); } } [JSInvokable] public void ToggleFlag() { // 原有ToggleFlag逻辑 } public void Dispose() { _dotNetRef?.Dispose(); } }
注意:非passive事件监听器会影响页面滚动性能,仅在必要的交互元素上使用。
内容的提问来源于stack exchange,提问作者Jaap
相关产品推荐
相关产品推荐

