angularionic-frameworkscrollbarionic4ion-content

How to detect if ion-content has a scrollbar?


I want to hide or show elements when there is or there isn't a scrollbar on ion-content. More specifically, I want to show a button (to load more items in a list) when there's no scrollbar and hide it where there is a scrollbar (so the loading of more items is done by ion-infinite-scroll).

My Ionic app will also be deployed to the desktop so users with large screens won't initially see a scrollbar and thus ion-infinite-scroll won't be triggered.

Here's a demo that showcases the issue:

home.page.html

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic header
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <div class="ion-padding">
    <p *ngFor="let item of itemList">{{ item }}</p>

    <!-- How to hide this button when ion-content has a scrollbar? -->
    <!-- *ngIf="???" -->
    <ion-button (click)="incrementItemList(5)">Load more items</ion-button>
  </div>

  <ion-infinite-scroll (ionInfinite)="loadMoreItems($event)">
    <ion-infinite-scroll-content loadingSpinner="crescent"></ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>

<ion-footer>
  <ion-toolbar>
    <ion-title>
      Ionic footer
    </ion-title>
  </ion-toolbar>
</ion-footer>

home.page.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  itemList: string[] = [];

  constructor() {}

  ionViewWillEnter() {
    this.incrementItemList(5);
  }

  incrementItemList(times: number) {
    for (let i = 1; i <= times; i++) {
      this.itemList.push(`Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia placeat nam sapiente iusto eligendi`);
    }
  }

  loadMoreItems(event: any) {
    setTimeout(() => {
      this.incrementItemList(15);
      event.target.complete();
    }, 1000);
  }

}

I'm using Ionic 4.5.0 + Angular.

I have tried using getScrollElement, scrollHeight, clientHeight, offsetHeight, but with no success.

Any ideas?


Solution

  • With the help of rtpHarry's post (thanks!), I finally came up with a proper solution for this use case:

    home.page.html

    <ion-content>
      <div class="ion-padding">
        <p *ngFor="let item of itemList">{{ item }}</p>
    
        <!--
          '*ngIf' removes the button from the DOM and changes the size of 'ion-content' which
          is problematic in some scenarios so I toggle the visibility CSS property instead
        -->
        <ion-button [style.visibility]="hasScrollbar ? 'hidden' : 'visible'" (click)="incrementItemList(5)">Load more items</ion-button>
      </div>
    
      <ion-infinite-scroll (ionInfinite)="loadMoreItems($event)">
        <ion-infinite-scroll-content loadingSpinner="crescent"></ion-infinite-scroll-content>
      </ion-infinite-scroll>
    </ion-content>
    

    home.page.ts

    import { Component, ViewChild, HostListener } from '@angular/core';
    import { IonContent } from '@ionic/angular';
    
    @Component({
      selector: 'app-home',
      templateUrl: 'home.page.html',
      styleUrls: ['home.page.scss'],
    })
    export class HomePage {
    
      hasScrollbar = false;
    
      itemList: string[] = [];
    
      @ViewChild(IonContent, {static: false}) private content: IonContent;
    
      // checks if there's a scrollbar when the user resizes the window or zooms in/out
      @HostListener('window:resize', ['$event'])
      onResize() {
        this.checkForScrollbar();
      }
    
      constructor() {}
    
      ionViewWillEnter() {
        this.incrementItemList(5);
      }
    
      incrementItemList(times: number) {
        for (let i = 1; i <= times; i++) {
          this.itemList.push(`Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia placeat nam sapiente iusto eligendi`);
        }
    
        this.checkForScrollbar();
      }
    
      loadMoreItems(event: any) {
        setTimeout(() => {
          this.incrementItemList(15);
          event.target.complete();
        }, 1000);
      }
    
      async checkForScrollbar() {
        const scrollElement = await this.content.getScrollElement();
        this.hasScrollbar = scrollElement.scrollHeight > scrollElement.clientHeight;
      }
    
    }