【问题标题】:How to detect if ion-content has a scrollbar?如何检测 ion-content 是否有滚动条?
【发布时间】:2021-08-14 06:14:51
【问题描述】:

我想在 ion-content 上有或没有滚动条时隐藏或显示元素。更具体地说,我想在没有滚动条时显示一个按钮(以在列表中加载更多项目)并在有滚动条的地方隐藏它(因此更多项目的加载由 ion-infinite-scroll 完成)。

我的 Ionic 应用程序也将部署到桌面,因此大屏幕用户最初不会看到滚动条,因此不会触发 ion-infinite-scroll。

这是一个展示该问题的演示:

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);
  }

}

我正在使用 Ionic 4.5.0 + Angular。

我尝试过使用getScrollElementscrollHeightclientHeightoffsetHeight,但没有成功。

有什么想法吗?

【问题讨论】:

  • 你能设置一个最初为假的标志,当你调用 loadMore 方法时你会使其为真?是的,如果它有效,那么您还需要在加载附加数据后再次将标志反转为 false。
  • @SunnyParekh 这不是一个万无一失的解决方案,因为屏幕大小因用户而异,并且在第一次调用 loadMore 方法后可能不会出现滚动条,因为可能没有足够的内容来触发滚动条。无论如何感谢您的帮助。

标签: angular ionic-framework scrollbar ionic4 ion-content


【解决方案1】:

更新:我睡在上面,然后意识到我完全错了。

这是一个工作示例。它会检查三个位置以确保不会错过出现的滚动条:

  1. 页面加载时
  2. 添加新内容时
  3. 当用户滚动时

在我的测试环境中,两次单击添加按钮时会出现在屏幕高度的位置,因此该按钮并不总是消失。我添加了ionScrollEnd 检查以提供额外的捕获方法。

您可以删除散布在代码周围的console.log,我将其保留以帮助您了解发生了什么。

示例页面:

<ion-header>
  <ion-toolbar>
    <ion-title>
      Scrollbar Test
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [scrollEvents]="true" (ionScrollEnd)="onScrollEnd()">
  <div class="ion-padding">
    <p *ngFor="let item of itemList">{{ item }}</p>

    <ion-button *ngIf="!hasScrollbar" (click)="addMoreItemsButtonClick(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>

示例代码:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { IonContent } from '@ionic/angular';

@Component({
  selector: 'app-scrollheight',
  templateUrl: './scrollheight.page.html',
  styleUrls: ['./scrollheight.page.scss'],
})
export class ScrollheightPage implements OnInit {

  public hasScrollbar: Boolean = false;

  @ViewChild(IonContent) private content: IonContent;

  itemList: string[] = [];

  constructor() { }

  ngOnInit() {
    // check at startup
    this.checkForScrollbar();
  }

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

  addMoreItemsButtonClick(quantity: number) {
    this.incrementItemList(quantity);

    // check after pushing to the list
    this.checkForScrollbar();
  }

  onScrollEnd() {
    // check after scrolling
    this.checkForScrollbar();
  }

  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);
  }

  checkForScrollbar() {
    this.content.getScrollElement().then((scrollElement) => {
      console.log("checking for scroll bar");
      console.log({scrollElement});
      console.log({scrollHeight: scrollElement.scrollHeight});
      console.log({clientHeight: scrollElement.clientHeight});
      this.hasScrollbar = (scrollElement.scrollHeight > scrollElement.clientHeight);
      console.log({hasScrollBar: this.hasScrollbar});
    });
  }
}

【讨论】:

  • 我现在已经用工作版本替换了我的答案。
  • @nunoarruda 这解决了问题吗?当我终于弄清楚这一点时,我真的很高兴。很高兴知道它是否对您有用。编码的第一天,我去吃午饭的时候实际上压力很大,因为我想不通,哈哈。
  • 不完全是,但它帮助我找到了适合我的用例的解决方案。非常感谢。
【解决方案2】:

在 rtpHarry 的帖子(谢谢!)的帮助下,我终于为这个用例想出了一个合适的解决方案:

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;
  }

}

【讨论】:

    【解决方案3】:

    您能否通过从@angular/core 导入来使用“NgZone” 示例代码在这里。

    .ts

    import { Component, OnInit, ViewChild, NgZone } from '@angular/core';
    @Component({
      selector: 'app-selector',
      templateUrl: './app.page.html',
      styleUrls: ['./app.page.scss'],
    })
    export class App implements OnInit {
    @ViewChild("listingContent") listingContent;
    public hasScrollbar: Boolean = false;
    public itemList:Array<any> = [];
        constructor(private zone: NgZone) {}
    
        scrollHandler(event) {
            this.zone.run(()=>{
                if (event.detail.scrollTop > (document.documentElement.clientHeight + 1)) {
                    this.hasScrollbar = true;
                } else {
                    this.hasScrollbar = false;
                }
            });
        }
    }
    

    .html

    <ion-header no-border></ion-header>
    <ion-content #listingContent padding-top (ionScroll)="scrollHandler($event)" scroll-events="true">
    <div class="ion-padding">
        <p *ngFor="let item of itemList">{{ item }}</p>
    
        <ion-button *ngIf="!hasScrollbar" (click)="addMoreItemsButtonClick(5)">Load more items</ion-button>
      </div>
    
    </ion-content>
    

    希望这会有所帮助..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      • 2011-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多