You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor中JavaScript IntersectionObserver事件触发两次的原因及解决

问题描述

在Blazor应用中使用JavaScript的IntersectionObserver实现滚动列表的分页懒加载,已通过断点确认监听器仅订阅一次,但每次触发时OnIntersectionSearch()都会执行两次。想明确事件触发两次的原因,以及解决方法。

相关代码

JavaScript代码

window.ObserverSearch = {
  observer: null,
    Initialize: function (component, observerId) {
      this.observer = new IntersectionObserver(e => {
        component.invokeMethodAsync('OnIntersectionSearch');
      });

      let element = document.getElementById(observerId);
      if (element == null) throw new Error("Target was not found");
      this.observer.observe(element);
  }
};

Blazor组件(InfiniteScrollSearch.razor)

@inject IJSRuntime JSRuntime
@ChildContent

Blazor组件代码文件(InfiniteScrollSearch.razor.cs)

public partial class InfiniteScrollSearch
{
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public string? ObserverSearchTarget { get; set; }

    [Parameter]
    public EventCallback<bool> ObservableSearchEndReached { get; set; }

    private DotNetObjectReference<InfiniteScrollSearch>? _objectRef;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _objectRef = DotNetObjectReference.Create(this);
            await JSRuntime.InvokeAsync<dynamic>("ObserverSearch.Initialize", _objectRef, ObserverSearchTarget);
        }
    }

    [JSInvokable]
    public async Task OnIntersectionSearch()
    {
        // 此处总是被调用两次
        await ObservableSearchEndReached.InvokeAsync(true);
    }
}

原因分析

IntersectionObserver的回调默认会在两种场景触发:

  • 目标元素进入视口时
  • 目标元素离开视口时

同时,默认配置下,只要元素有部分进入视口就会触发回调,这就是你遇到两次调用的核心原因。

解决方案

1. 过滤交叉状态,仅处理进入视口事件

在JavaScript回调中检查交叉条目isIntersecting属性,仅当元素进入视口时调用Blazor方法:

window.ObserverSearch = {
  observer: null,
  Initialize: function (component, observerId) {
    this.observer = new IntersectionObserver(entries => {
      const targetEntry = entries[0];
      // 仅当元素进入视口时触发
      if (targetEntry.isIntersecting) {
        component.invokeMethodAsync('OnIntersectionSearch');
      }
    });

    let element = document.getElementById(observerId);
    if (element == null) throw new Error("Target was not found");
    this.observer.observe(element);
  }
};

2. 配置观察选项,精准控制触发时机

可以给IntersectionObserver添加配置参数,比如设置threshold(触发阈值)或rootMargin(提前触发距离):

this.observer = new IntersectionObserver(entries => {
  const targetEntry = entries[0];
  if (targetEntry.isIntersecting) {
    component.invokeMethodAsync('OnIntersectionSearch');
  }
}, {
  threshold: 1.0, // 元素完全进入视口才触发
  rootMargin: '0px 0px 100px 0px' // 可选:元素距离底部还有100px时就触发,提前加载数据
});

3. 添加加载锁,避免重复触发(可选)

在Blazor方法中添加加载状态锁,防止短时间内重复调用:

private bool _isLoading = false;

[JSInvokable]
public async Task OnIntersectionSearch()
{
    if (_isLoading) return;
    _isLoading = true;
    try
    {
        await ObservableSearchEndReached.InvokeAsync(true);
    }
    finally
    {
        _isLoading = false;
    }
}

内容的提问来源于stack exchange,提问作者Frank Mehlhop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 15:15:27