Scrolling to bottom of div without using setTimeou

2020-04-18 03:56发布

问题:

I am making an internal live chat system using Firebase. I make this call to get a list of all chat messages:

firebase.database().ref('chatrooms/'+this.roomkey+'/chats').on('value', resp => {

    this.chats = [];
    this.chats = snapshotToArray(resp);

    setTimeout(() => {
        if(this.content._scroll) { this.content.scrollToBottom(0); }
    }, 1000);

});

In my view I have this HTML to loop through the chats:

<ion-content>
  <ion-list>
    <ion-item *ngFor="let chat of chats" no-lines>
      <div class="chat-status" text-center *ngIf="chat.type==='join'||chat.type==='exit';else message">
        <span class="chat-date">{{chat.sendDate | date:'short'}}</span>
        <span class="chat-content-center">{{chat.message}}</span>
      </div>
      <ng-template #message>
        <div class="chat-message" text-right *ngIf="chat.user === nickname">
          <div class="right-bubble">
            <span class="msg-name">Me</span>
            <span class="msg-date">{{chat.sendDate | date:'short'}}</span>
            <p text-wrap>{{chat.message}}</p>
          </div>
        </div>
        <div class="chat-message" text-left *ngIf="chat.user !== nickname">
          <div class="left-bubble">
            <span class="msg-name">{{chat.user}}</span>
            <span class="msg-date">{{chat.sendDate | date:'short'}}</span>
            <p text-wrap>{{chat.message}}</p>
          </div>
        </div>
      </ng-template>
    </ion-item>
  </ion-list>
</ion-content>

And here is my relevant CSS:

ion-content {
  height: 100%;
  overflow: hidden;
  .item-md, .item-ios {
    background: none;
  }
}

There are currently 30 chat messages. They are loaded from Firebase and then after a second, when the setTimeout finishes, the user is automatically scrolled to the bottom of the page so they can see the most recent message. This makes the page load a bit odd with the jump down to the bottom of the page after a second.

Is there any way to replace the timeout with something that will achieve the goal of the user initially seeing the most recent messages? Maybe there is some trigger that can be made that detects that chats has changed in the view and then does the scroll? This would seem better than a fixed 1 second pause?

回答1:

You could scroll to the bottom of the list when changes to the list of chats are detected:

  • Associate a template reference variable to the ion-item elements (e.g. #chatItems)
  • Use ViewChildren to get the QueryList of ion-item elements
  • In ngAfterViewInit, subscribe to the QueryList.changes event
  • Scroll to the bottom of the list when the list changes
<ion-content>
  <ion-list>
    <ion-item #chatItems *ngFor="let chat of chats" no-lines>
      ...
    </ion-item>
  </ion-list>
</ion-content>
export class MyComponent {

  @ViewChildren("chatItems") chatItems: QueryList<any>;

  ngAfterViewInit() {
    this.scrollContentToBottom();
    this.chatItems.changes.subscribe(() => {
      this.scrollContentToBottom();
    });
  }

  scrollContentToBottom() {
    if(this.content._scroll) { 
      this.content.scrollToBottom(0); 
    }
  }
  ...
}

Note: I used QueryList<any> because I don't know the ion-item component type. You can put the appropriate type if you know what it is.