43

I was looking for a solution, but nothing found (only articles about (window:resize), but it is not I am looking for).

How to detect element size change in Angular 2?

<div #myElement (sizeChanged)="callback()" />

I want to use some CSS animations and detect element's height and width changes.

1
  • Have you solved it sir?? Because I also want same thing. Commented Feb 16, 2017 at 9:49

6 Answers 6

27

Edit: Modern answer

Modern browsers support the ResizeObserver API. The API has been out for a number of years and is now widely supported. Unfortunately, Edge only supports it now that it's on Chromium. If that matters to you, look at the old answer or use a polyfill.

If you're interested, here goes. You want to create a ResizeObserver object and call observe on it. In this example, the blue block keeps resizing as we cycle the text and padding. The current size of the element is added to the unordered list.

const btn = document.getElementById('btn');
const container = document.getElementById('container');
const output = document.getElementById('output');
btn.onclick = () => {
  if (index > 2) {
    if (container.className === '') {
      container.className = 'high';
    } else {
      container.className = '';
    }
    index = 0;
  }
  container.innerText = values[index++];
}

let index = 0;
const values = [
  'Short',
  'Longer text',
  'Very much longer text of the kind that will fill a large container',
];

function createEntry(text) {
const li = document.createElement('li');
    li.innerText = text;
    output.appendChild(li);
}

let obs = new ResizeObserver(entries => {
  console.log(entries)
  for (let entry of entries) {
    const cr = entry.contentRect;
    createEntry(`Element size: ${cr.width}px x ${cr.height}px`)
  }
});
obs.observe(container);
#container {
  display: inline-block;
  background: lightblue;
}
.high {
  padding: 1rem;
}
<div>
  <button id="btn">Cycle</button>
</div>
<div id="container">Test This</div>
<ul id="output">
</ul>

Original answer

The problem is not even an Angular problem. More generally, how do you detect size changes in any element other than window? There is an onresize event, but this is only triggered for window and there are no other obvious solutions.

The general way that many approach this, is to set an interval of, say, 100ms and check the width and height of the div to detect a change. As horrible as it sounds, this is the most common approach.

From this answer to the more general question, there is a library to do this using only events: http://marcj.github.io/css-element-queries/. Supposedly, it's quite good. You would use the ResizeSensor to get what you're looking for.

Unless of course, you're only expecting the div to resize when the window does. Then onresize is what you're looking for.

Sign up to request clarification or add additional context in comments.

5 Comments

Any updates on it? Are better solutions available 1.5 years later using Angular 2-6?
Honestly @tom I haven't looked into it at all since. I don't know of any new browser API, but even if such a thing existed you would still be stuck having to support IE11 (it's more popular than Edge and not updated at all).
@tom I think the solution by Sufiyan Ansari below is a newer better solution.
There is a better solution now: web.dev/resize-observer
Thanks @JamesHollyer for the reminder. I've wanted to update this for a while.
25

Detecting a change in any element of the angular component. We can use a ResizeObserver (class from import ResizeObserver from 'resize-observer-polyfill'; ) without library.

here is my implementation:

Import:

import ResizeObserver from 'resize-observer-polyfill';

Implementation:

@ViewChild('divId') //eg: <div #divId><div> 
public agNote: ElementRef; //Element Reference on which the callback needs to be added

/**
   * this will bind resize observer to the target element
   */
  elementObserver() {
    var ro = new ResizeObserver(entries => {
      for (let entry of entries) {
        const cr = entry.contentRect;
        console.log('Element:', entry.target);
        console.log(`Element size: ${cr.width}px x ${cr.height}px`);
        console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
        console.log($event);

      }
    });

    // Element for which to observe height and width 
    ro.observe(this.agNote.nativeElement);
  }

1 Comment

If u observing a custom Angular element (for example: Component's host element) don't forget to add 'display: block' style to it. Because its 'inline' by default and dont have proper width/height values.
11

For angular users, You can easily apply this code....

Html

<div (resized)="onResized($event)"></div>

Angular

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

// Import the resized event model
import { ResizedEvent } from 'angular-resize-event';

@Component({...})
class MyComponent {
  width: number;
  height: number;

  onResized(event: ResizedEvent) {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}

Remember you have to install node module and import it to your app.module.ts file.

Check this link for more: https://www.npmjs.com/package/angular-resize-event

Comments

6

Two scenarios must be detected:

  1. The element is modified
  2. The window is resized

Since angular if often modifying the element (adding classes, etc.) it's important to wait until changes are "done". Observables can be used for this.

DEMO: https://stackblitz.com/edit/angular-mutationobserver-example-tmafmw

JS Code:

import { Component ,HostListener, AfterViewInit, ViewChild, ElementRef, OnInit, OnDestroy } from '@angular/core';
import { AppService } from './app.service';
import { Subscription, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

class HeightAndWidth{
  height:number;    
  width:number;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  public elements: string[];
  public height:number = 0;
  public width:number = 0;

  constructor(private appService: AppService) {
    this.elements = ['an element', 'another element', 'who cares'];
  }

  addElement(): void {
    this.elements.push('adding another');
  }

  removeElement(index: number): void {
    this.elements.splice(index, 1);
  }

  private subscription: Subscription;
  @ViewChild('divToTrackHeightChanges') divToTrackHeightChanges:ElementRef;  

  @HostListener('window:resize', ['$event'])
  onResize(event) {
    this.doDivHeightChange(this.getHeightAndWidthObject());    
  }

  getHeightAndWidthObject():HeightAndWidth{
    const newValues = new HeightAndWidth();
    newValues.height = this.divToTrackHeightChanges.nativeElement.offsetHeight;
    newValues.width = this.divToTrackHeightChanges.nativeElement.offsetWidth;
    return newValues;
  }
  setupHeightMutationObserver() {
    const observerable$ = new Observable<HeightAndWidth>(observer => {
      // Callback function to execute when mutations are observed
      // this can and will be called very often
      const callback = (mutationsList, observer2)=> {
        observer.next(this.getHeightAndWidthObject());
      };
      // Create an observer instance linked to the callback function
      const elementObserver = new MutationObserver(callback);

      // Options for the observer (which mutations to observe)
      const config = { attributes: true, childList: true, subtree: true };
      // Start observing the target node for configured mutations
      elementObserver.observe(this.divToTrackHeightChanges.nativeElement, config);      
    });

    this.subscription = observerable$
      .pipe(
        debounceTime(50),//wait until 50 milliseconds have lapsed since the observable was last sent
        distinctUntilChanged()//if the value hasn't changed, don't continue
      )
      .subscribe((newValues => {
        this.doDivHeightChange(newValues);
      }));
  }

  doDivHeightChange(newValues:HeightAndWidth){
   this.height = newValues.height;
   this.width = newValues.width;
  }

  ngAfterViewInit() {
    this.setupHeightMutationObserver();
    this.doDivHeightChange(this.getHeightAndWidthObject());
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

HTML Code

<div #divToTrackHeightChanges>
    <h1>Here is a div that changes</h1>    
    <span style="width:200px;display:inline-block;" *ngFor="let element of elements; let i = index">
      Element <button (click)="removeElement(i)">Remove</button>
    </span>
    <br/>
    <br/>  
  <button (click)="addElement()">Click me enough times to increase the divs height!</button>
</div>
<div>The div above has a height of  {{height}} and width of {{width}}</div>
<div>RESIZE the window to test as well</div>

1 Comment

Your code is going to work only in case of window is get resize. What user is looking out for component resize which could happen not only on window resize but also once the component is fully loaded. For example grid component initial height is 100px while loading the rows from the backend and once loaded size is increase to 400px. In this case window resize is not happening only div/component height is getting changed. Here ResizeObeserver is the good choice.
3

(2024 answer) I've found a way to subscribe to changes to an element's size without needing to import a plugin/library.

The simplest example I can give is as follows

export class ExpandIfEllipsisComponent implements AfterViewInit, OnDestroy {
  @ViewChild('textEl') textEl: ElementRef;
  observer: ResizeObserver;

  ngAfterViewInit(): void {
    this.setUpResizeObserver();
  }

  ngOnDestroy() {
    this.observer.unobserve(this.textEl.nativeElement);
  }
  
  setUpResizeObserver(): void {
    this.observer = new ResizeObserver(_ => {
      this.useNewSizes();
    });

    this.observer.observe(this.textEl.nativeElement);    
  }

  useNewSizes() {
    const element = this.textEl.nativeElement;
    // Updated size values:
    console.log(element.offsetHeight, element.scrollHeight, element.offsetWidth, element.scrollWidth)
  }
  
}

Then in your html

  <span #textEl>Your text content</span>

It's based on this article, which provides more detail.

Comments

-5

A really graceful solution similar to the top answer using RxJS...

HTML

<element-to-watch #ref></element-to-watch>

Component or Directive

export class MyComponentOrDirective implements OnInit, OnDestroy {
  private interval = interval(100);      
  private intervalSub = Subscription;      

  constructor(private ref: ElementRef) {}

  ngOnInit(): void {
    this.watchElementChanges();
  }

  ngOnDestroy(): void {
    if (this.intervalSub) {
      this.intervalSub.unsubscribe();
    }
  }

  private watchElementChanges(): void {
    this.intervalSub.subscribe(() => {
      // drag window around and watch the sizes change
      console.log(this.ref.nativeElement.clientWidth);
      console.log(this.ref.nativeElement.clientHeight);
    });
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.