Virtualize regression in .NET 11: Home and End typed in a text input jump the list to its first or last item

オープン
#70,554 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
55/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
csharp, javascript
領域
frontend

調査の方向性

Start by reading Virtualize.ts and its handleJumpKeys keydown handler; the issue identifies this as the source of the regression and links its introduction to #64964. Reproduce with the supplied Blazor pages, especially an input inside a virtualized list, and compare Home and End behavior with .NET 10. Done when typing those keys in editable controls leaves the list position and focus intact while list navigation still works when the list itself has focus.

索引モデルが issue の本文から書いたものです。

説明

area-blazor Regression
Is there an existing issue for this?
  • I have searched the existing issues
Describe the bug

.NET 11's Virtualize listens for keydown on its scroll container, or on document when the page itself scrolls, and treats every Home and End as a request to jump to the start or end of the list (handleJumpKeys in Virtualize.ts). It came with #64964 in 11.0-preview3; .NET 10 had no key handling in Virtualize.ts. It doesn't check where the key came from, so Home and End typed in a text input also jump the list instead of only moving the caret:

  • Input inside the list: the list jumps and the row being edited is virtualized away. With @key its input is removed and focus falls back to <body>. Without @key, as in the first page below, Blazor reuses the focused input for another item, so the caret ends up in a different row (Name 977 after End from Name 204).
  • List that scrolls with the page: End in any input on the page, such as a search box above the list, scrolls the page to the end of the list.

The handler also runs for keys a component has already handled. A list component that implements Home and End itself and calls preventDefault() still gets the jump on top of its own behaviour.

On .NET 10 these keys only move the caret. Lists with inputs in or around them are common: editable grids, filter rows in a sticky header, forms over long collections, search boxes. QuickGrid has it too: in a virtualized QuickGrid with a search box in ColumnOptions, like the column options example in the QuickGrid docs, Home and End typed in the search box jump the grid to its first or last row (third page below). We found this through MudBlazor's MudDataGrid (cell editing and the column filter row) and MudTable with text fields in rows.

Expected Behavior

As on .NET 10.0.15, Home and End in a text input move the caret and the list stays where it is.

Input inside the list (first page below), scrolled to 6000 px, after clicking the input of row 204:

Runtime End Home
.NET 10.0.15 stays at 6000 px, caret at end of "Name 204" stays at 6000 px, caret at start
.NET 11.0.0 jumps to the end (29700 px), focus in "Name 977" jumps to the top (0 px), focus in "Name 21"

Input above a list that scrolls with the page (second page below), after clicking the input: End leaves the page at 0 px on .NET 10.0.15 and scrolls it to 29279 px, the end of the list, on .NET 11.0.0.

QuickGrid search box in ColumnOptions (third page below), grid scrolled to 2768 px: End leaves the grid there on .NET 10.0.15 and jumps it to 31633 px (row 992) on .NET 11.0.0. Home jumps it to 0 px.

Returning early from handleJumpKeys when e.defaultPrevented is set or e.target is inside an input, textarea, select or editable contenteditable element makes .NET 11.0.0 match .NET 10 on all three pages. End and Home pressed while the list itself has focus, for example after clicking a QuickGrid cell, still jump. I tested that by rewriting blazor.web.js in flight; I have not built aspnetcore. The scroll keys that isUserScrollKey matches come through the same handler from inputs, so the check may belong before those as well.

Steps To Reproduce
  1. dotnet new blazor --interactivity Server --empty, and replace Components/Pages/Home.razor with the first page below.
  2. Scroll the list a little, click into one of the inputs, and press End or Home.
  3. The list jumps to its last or first item, and the caret is now in another row's input.
@page "/"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Components.Web.Virtualization

<p>@System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription</p>

<div style="height: 300px; overflow-y: auto; border: 1px solid #999;">
    <Virtualize Items="_names" Context="name" ItemSize="30">
        <div style="height: 30px;"><input value="@name" /></div>
    </Virtualize>
</div>

@code {
    private readonly List<string> _names = Enumerable.Range(0, 1000).Select(i => $"Name {i}").ToList();
}

For the page-scroll case, click into the input and press End:

@page "/window"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Components.Web.Virtualization

<p>@System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription</p>

<input value="Search text" />

<Virtualize Items="_names" Context="name" ItemSize="30">
    <div style="height: 30px;">@name</div>
</Virtualize>

@code {
    private readonly List<string> _names = Enumerable.Range(0, 1000).Select(i => $"Name {i}").ToList();
}

For QuickGrid, add the Microsoft.AspNetCore.Components.QuickGrid package, scroll the grid, open the Name column's options, click into the search box and press End or Home:

@page "/quickgrid-filter"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Components.QuickGrid

<p>@System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription</p>

<style>
    #grid { height: 300px; overflow-y: auto; }
    #grid table { min-width: 100%; }
    #grid thead { position: sticky; top: 0; background-color: #d8d8d8; z-index: 1; }
    #grid tr { height: 30px; }
</style>

<div id="grid">
    <QuickGrid Items="FilteredPeople" Virtualize="true" ItemSize="30">
        <PropertyColumn Property="@(p => p.Id)" />
        <PropertyColumn Property="@(p => p.Name)">
            <ColumnOptions>
                <input type="search" @bind="_filter" @bind:event="oninput" placeholder="Filter names..." />
            </ColumnOptions>
        </PropertyColumn>
    </QuickGrid>
</div>

@code {
    private record Person(int Id, string Name);

    private readonly List<Person> _people = Enumerable.Range(0, 1000).Select(i => new Person(i, $"Name {i}")).ToList();
    private string _filter = "";

    private IQueryable<Person> FilteredPeople => _people.Where(p => p.Name.Contains(_filter)).AsQueryable();
}

To compare with .NET 10, target net10.0;net11.0, replace <BasePath /> in App.razor with <base href="/" /> and add app.UseAntiforgery(); before app.MapStaticAssets();. For the QuickGrid page, reference QuickGrid 10.0.0 for net10.0 and 11.0.0 for net11.0.

Exceptions (if any)

None.

.NET Version

SDK 11.0.100, runtime 11.0.0 (ASP.NET Core 11.0.0). Compared with ASP.NET Core 10.0.15. Chrome 157 (headless, driven through the DevTools protocol), Windows 11.

Anything else?

handleJumpKeys is unchanged on main apart from the added interruptProgrammaticScroll() calls.

主要言語
C#
スター
38.5k
フォーク
11.6k
平均マージ
3日 1時間
マージ済み PR(30日)
289

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

dotnet/aspnetcore のほかの issue

dotnet/aspnetcore の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。