cup_of
cup_of

Reputation: 6697

Angular way to tell if content overflows container in ngFor, and if so which item starts the overflow

I'd like a way to tell if the components inside of a container overflow the container. If it does overflow, I'd like to get the index of the item that causes the overflow.

The goal of this problem is to add a show more button when it overflows and to split the object into two; the items that fit, and the items that don't.

The components are generated by *ngFor.

Here's a minimal example of what's going on

<div class="my-container">

  <div 
    class="item"
    *ngFor="let item of items; let i = index; trackBy: trackById"
  >
    {{ item.val }}
  </div>

<div>
.my-container {
  overflow: hidden;
  max-width: 100%;
  display: flex;
}

As my container fills up horizontally with the items, it can overflow horizontally if there's enough items. I'd like to tell if it does overflow, and determine the index of which item causes the overflow.

Here's my attempt:

  // this fn gets called in ngOnChanges

  determineShowMore() {
    const container = document.querySelector('.my-container') as HTMLElement;
    const viewItems: any = container ? Array.from(viewsContainer.children) : [];

    let totalWidth = 0;

    viewItems.forEach((x: HTMLElement) => {
      totalWidth += x.offsetWidth;
    });

    const isOverflow = totalWidth > viewsContainer.offsetWidth ? true : false;

  // missing: determine which item causes the overflow
  }

It doesn't seem to work well, maybe because it's happening before the html is fully rendered? I can't run this fn on ngAfterViewInit() because my items can change dynamically and I need it to run every time that changes.

Upvotes: 0

Views: 1446

Answers (2)

user1971419
user1971419

Reputation:

First of all, I would not use document.querySelector() if you're using Angular. You can import ElementRef and use that instead. Secondly, I think you can achieve this by running a callback each time one element is added. Try creating a subcomponent out of a single item and use an event emitter to tell the parent whether or not to add a read more button.

import { Component, ElementRef, Output, EventEmitter } from '@angular/core'

@Component({
  selector: 'single-item',
  templateUrl: './single-item.component.html',
  styleUrls: [ './single-item.component.css' ]
})
export class SingleItem {

  @Input index: number
  @Output onAddItem = new EventEmitter()

  constructor(private elementRef: ElementRef) {
    const container = this.elementRef.nativeElement
    const wrapper = this.container.parentElement
    this.onAddItem.emit({
      isOverflowed:
        container.clientHeight > wrapper.clientHeight ||
        container.clientWidth > wrapper.clientWidth,
      index: this.index:
    })
  }

}

And then in the parent's HTML:

<div class="container">
  <ng-container
    *ngFor="let item of items; let i = index; trackBy: trackById"
  >
    <single-item
      (onAddItem)="handleAddItem($event)"
      [index]="i"
    >
      {{ item.val }}
    </single-item>
  </ng-container>
</div>

In the JS, you might handle it like this.

  overflowingIndex: number

  handleAddItem(event) {
    if (!event.isOverflowed) return false
    this.overflowingIndex = this.overflowingIndex || event.index
    // add your read more button here
  }

Upvotes: 3

Hitech Hitesh
Hitech Hitesh

Reputation: 1635

You can use the Intersection Observer api for it.

It will help you in to notifying whether which div item is visible full or partially. The docs link and the angular implementation link is below.

https://usefulangle.com/post/113/javascript-detecting-element-visible-during-scroll

https://blog.angularindepth.com/a-modern-solution-to-lazy-loading-using-intersection-observer-9280c149bbc

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

Upvotes: 1

Related Questions