Blazor Server实现DOM元素尺寸监听的问题排查与解决
本人不擅长Javascript与Blazor结合开发,此前借助谷歌及AI工具实现了浏览器尺寸变更时触发Blazor应用代码的事件监听器。现需为指定<div id="abc">实现相同的尺寸监听功能,以便组件设置像素单位的宽高。
Blazor组件代码
@inject DivSizeService dService .. html .. @code { .. protected override async Task OnInitializedAsync() { dService.OnDivSizeChanged += HandleDivSizeChanged; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await dService.StartListeningForDivSizeChanges("abc"); await UpdateDivSize(); } } private async void HandleDivSizeChanged(object sender, DivSizeChangedEventArgs e) { if (e.ElementId == "abc") { await UpdateDivSize(); } } async Task UpdateDivSize() { divSize = await dService.GetDivSize("abc"); Style = "Width: " + (divSize.Width - 50).ToString() + "px; Height: " + (divSize.Height - 50).ToString() + "px;"; ChildComponent.Refresh // pseudocode } }
DivSizeService服务代码
public class DivSizeService { private readonly IJSRuntime _js; public event EventHandler<DivSizeChangedEventArgs> OnDivSizeChanged; public DivSizeService(IJSRuntime js) { _js = js; } public async Task<DivSize> GetDivSize(string elementId) { return await _js.InvokeAsync<DivSize>("getElementDimensions", elementId); //var dimensions = await _js.InvokeAsync<DivSize>("getDimensionsA"); //return new DivSize { Width = dimensions.Width, Height = dimensions.Height }; } public async Task StartListeningForDivSizeChanges(string elementId) { await _js.InvokeVoidAsync("registerResizeListener", elementId, DotNetObjectReference.Create(this)); // updated } [JSInvokable] public void HandleDivSizeChanged(string elementId, DivSize newSize) { OnDivSizeChanged?.Invoke(this, new DivSizeChangedEventArgs(elementId, newSize)); } } public class DivSizeChangedEventArgs : EventArgs { public string ElementId { get; } public DivSize NewSize { get; } public DivSizeChangedEventArgs(string elementId, DivSize newSize) { ElementId = elementId; NewSize = newSize; } }
Javascript代码
window.getElementDimensions = function (elementId) { const element = document.getElementById(elementId); return { width: element ? element.offsetWidth : 0, height: element ? element.offsetHeight : 0 }; }; window.registerResizeListener = (element, dotnetReference) => { const observer = new ResizeObserver(entries => { for (let entry of entries) { const rect = entry.contentRect; console.log(entry.contentRect); const size = { width: rect.width, height: rect.height }; dotnetReference.invokeMethodAsync('DivSizeService.HandleDivSizeChanged', element, size); } }); observer.observe(document.getElementById(element)); }
运行报错
Exception thrown: 'Microsoft.JSInterop.JSException' in System.Private.CoreLib.dll
Exception thrown: 'Microsoft.JSInterop.JSException' in System.Private.CoreLib.dll
Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer: Warning: Unhandled exception rendering component: Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element'.
TypeError: Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element'.
已知window.registerResizeListener中接收到的element为"abc",推测是参数传递方式有误,导致observer.observe(element)报错。
更新1:
调整后Javascript可识别<div id="abc">的尺寸变化,但出现新错误:
Uncaught Error Error: System.ArgumentException: The type 'DivSizeService' does not contain a public invokable method with [JSInvokableAttribute("DivSizeService.HandleDivSizeChanged")].
更新2:问题解决!
由于已向Javascript传递了dotNetReference(指向DivSizeService实例),无需在调用方法时添加类名前缀。将Javascript代码中的调用语句:
dotnetReference.invokeMethodAsync('DivSizeService.HandleDivSizeChanged', element, size);
修改为:
dotnetReference.invokeMethodAsync('HandleDivSizeChanged', element, size);
问题得以解决。
内容的提问来源于stack exchange,提问作者Tobias Gustafsson

