Skip to content
13 changes: 11 additions & 2 deletions core/src/components/infinite-scroll/infinite-scroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ export class InfiniteScroll implements ComponentInterface {
private thrPx = 0;
private thrPc = 0;
private scrollEl?: HTMLElement;

/**
* didFire exists so that ionInfinite
* does not fire multiple times if
* users continue to scroll after
* scrolling into the infinite
* scroll threshold.
*/
private didFire = false;
private isBusy = false;

Expand Down Expand Up @@ -127,8 +135,6 @@ export class InfiniteScroll implements ComponentInterface {
this.ionInfinite.emit();
return 3;
}
} else {
this.didFire = false;
}

return 4;
Expand Down Expand Up @@ -190,10 +196,13 @@ export class InfiniteScroll implements ComponentInterface {
writeTask(() => {
scrollEl.scrollTop = newScrollTop;
this.isBusy = false;
this.didFire = false;
});
});
});
});
} else {
this.didFire = false;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Infinite Scroll - Small DOM Update</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
#list .item {
width: 100%;
border-bottom: 1px solid gray;

padding: 10px;
}
</style>
</head>

<body>
<ion-app>
<ion-content class="ion-padding" id="content">
<div id="list"></div>

<ion-infinite-scroll threshold="100px" id="infinite-scroll">
<ion-infinite-scroll-content loading-spinner="crescent" loading-text="Loading more data...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
</ion-app>

<script>
const list = document.getElementById('list');
const infiniteScroll = document.getElementById('infinite-scroll');

infiniteScroll.addEventListener('ionInfinite', () => {
setTimeout(() => {
appendItems();

infiniteScroll.complete();

// Custom event consumed in the e2e tests
window.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
}, 500);
});

function appendItems(count = 3) {
for (var i = 0; i < count; i++) {
const el = document.createElement('div');
el.classList.add('item');
el.textContent = `${1 + i}`;
list.appendChild(el);
}
}
appendItems(30);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('infinite-scroll: appending small amounts to dom'), () => {
test('should load more after remaining in threshold', async ({ page }) => {
await page.goto('/src/components/infinite-scroll/test/small-dom-update', config);

const ionInfiniteComplete = await page.spyOnEvent('ionInfiniteComplete');
const content = page.locator('ion-content');
const items = page.locator('#list .item');
expect(await items.count()).toBe(30);

await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
await ionInfiniteComplete.next();

/**
* Even after appending we'll still be within
* the infinite scroll's threshold
*/
expect(await items.count()).toBe(33);

await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
await ionInfiniteComplete.next();

/**
* Scrolling down again without leaving
* the threshold should still trigger
* infinite scroll again.
*/
expect(await items.count()).toBe(36);
});
});
});