Skip to content

feat: support ion-infinite-scroll in a flex-direction: column-reverse scroll element #31602

Open

Description

@tzdevelopteam

Ionic Framework Version

v9.x (9.0.7)

Current Behavior

A chat-style page lays the scroll element out bottom-up with ion-content::part(scroll) { display: flex; flex-direction: column-reverse; } so that the newest row sits at the bottom and the scroller starts there. In that layout the browser's scrollTop is 0 at the bottom and goes negative towards the top ([-320, 0] in the reproduction).

ion-infinite-scroll computes its distance as if scrollTop were non-negative (core/src/components/infinite-scroll/infinite-scroll.tsx L121–129):

const distanceFromInfinite =
  this.position === 'bottom'
    ? scrollHeight - infiniteHeight - scrollTop - threshold - height
    : scrollTop - infiniteHeight - threshold;

With the trigger placed at the oldest end of the list (the last DOM child, painted at the top) and threshold="100px":

position what happens
top ionInfinite fires on the very first scroll step, 315 px away from the oldest end, and again on every later step (7 of 7 steps fired)
bottom never fires, including at scrollTop = -320, i.e. with the trigger fully on screen at the oldest end

Scroller in the reproduction: scrollHeight 1164, clientHeight 844, so the whole scrollable range is 320 px.

connectedCallback (L96–101) and complete() (L184–200) also write scrollEl.scrollTop = scrollHeight - clientHeight / scrollHeight - prev for position="top", which are positions outside the [-320, 0] range of a column-reverse scroller.

Expected Behavior

The trigger fires once when the user is within threshold of the trigger element, whichever direction the scroll element grows in.

Steps to Reproduce

  1. <ion-content> with the style above, 60 rows of 48 px, and <ion-infinite-scroll position="top" threshold="100px"> as the last child (so it paints at the top).
  2. Listen to ionInfinite, call complete() in the handler, and set scrollTop to -5, -20, -60, … on the scroll element returned by getScrollElement().
  3. Observe one ionInfinite per step. Switch to position="bottom" and scroll all the way up: no event.

Ionic Info

@ionic/core 9.0.7, Chromium 142 (Playwright headless).

Activity

  1. changed the title [-]bug: ion-infinite-scroll fires continuously (position="top") or never (position="bottom") when the scroll element is `flex-direction: column-reverse`[/-] [+]feat: support ion-infinite-scroll in a `flex-direction: column-reverse` scroll element[/+] on Oct 28, 2026
  2. thetaPC commented on Oct 28, 2026

    @thetaPC
    Contributor

    Thanks for the report!

    Verified on 9.0.10. Your analysis is right: ion-infinite-scroll measures its distance to the trigger as if scrollTop were never negative. In a column-reverse scroller it is 0 at the bottom and negative towards the top, so position="top" fires on every scroll and position="bottom" never fires. The scroll correction in complete() also writes positions outside the reversed range.

    Workarounds, both verified on 9.0.10:

    • Keep the column-reverse scroller and replace ion-infinite-scroll with your own scroll listener. A reversed scroller keeps the visible messages in place when older ones are added, so no scroll correction is needed:
    const content = document.querySelector('ion-content');
    const scrollEl = await content.getScrollElement();
    let loading = false;
    
    const loadOlderMessages = async () => {
      // Fetch older messages and append them after the existing rows in the DOM
    };
    
    scrollEl.addEventListener('scroll', async () => {
      const distanceFromOldest = scrollEl.scrollHeight - scrollEl.clientHeight - Math.abs(scrollEl.scrollTop);
      if (loading || distanceFromOldest > 100) {
        return;
      }
      loading = true;
      await loadOlderMessages();
      loading = false;
    });

    This replaces the earlier answer on #28182 that column-reverse is not supported. Keeping this open to track column-reverse support in ion-infinite-scroll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      ⌂ Proxy