How to get width of (DOM) Element in Angular2

81,957

Solution 1

I don't know a way to get the width from the host element without accessing nativeElement but setting could be done like:

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

@HostBinding('style.height.px')
elHeight:number;

private resizeWorks(): void {
  this.elHeight = this.el.nativeElement.width;
}

If you can add an element inside your components template like

<div style="width: 100%;" #div (window:resize)="elHeight = div.getBoundingClientRect()">
  <!-- your template here -->
</div>

then this would work without direct DOM access at all (but not after init).

Solution 2

I tried out @GünterZöchbauer solution and refined it. You do not need HostListener to get bounds of div. Like with 'click' or other event (event)="function()", it will fire this function. I hope someone will find this helpful.

<div #div (window:resize)="onResize(div.getBoundingClientRect())">
   <!-- your template here -->
</div>


onResize() { 
   this.resizeWorks();
}

@HostBinding('style.height.px') elHeight:number;

private resizeWorks(): void {
   this.elHeight = this.el.nativeElement.width;
}

(#div) - this is variable needed to get measurement target. It does not have to be same name as element, it can be any name.

One more way to get element dimensions is to use ElementRef class from Angular Core, but then you have to find that child element which has width and height properties. I used this in Angular 2 Maps to get container width and height, but i found out that, using this method i had to find in element tree element which had these properties and it was second child of root element. It is more messy. ElementDimensions - is just a class with height and width which I made to contain those properties.

<div (window:resize)="onResize()">
   <!-- your template here -->
</div>

private getContainerDimensions(): ElementDimensions{
    var rootElement = this.elementRef.nativeElement;
    var childElement = rootElement.firstElementChild;
    var contentElement =  childElement.firstElementChild;

    return {
        height:contentElement.clientHeight,
        width: contentElement.clientWidth
    };
}

Solution 3

I tested this way and simply worked! It seems that binding did this job.

<div #foto [style.height.px]="foto.getBoundingClientRect().width / 2">

Solution 4

You can get the width of the component itself if that one has a display property defined (such as 'block' in the example below). Note that you should be able to define this style (using dom shadowing):

:host {
    display:block;
}

If you are not using dom shadowing (Ionic 2-3):

my-component {
    display:block;
}

Here is the controller (exposing the width as an observable):

import {AfterViewInit, Component, ElementRef, HostListener, Input} from '@angular/core';
import {Subject} from 'rxjs/Subject';

@Component({
    selector: 'my-component',
    template: '{{$width|async}}',
    style: 'my-component {display: block;}'
})
export class MyComponent implements AfterViewInit {
    @Input() public gridWidth: number;
    private $width: Subject<number> = new Subject();

    constructor(private elementRef: ElementRef) {
    }

    public ngAfterViewInit(): void {
        this.emitWidth();
    }

    @HostListener('window:resize', ['$event.target'])
    public onResize(): void {
        this.emitWidth();
    }

    private emitWidth(): void {
        this.$width.next(this.getWidth());
    }

    private getWidth(): number {
        return this.getNativeElement().clientWidth;
    }

    private getNativeElement(): HTMLElement {
        return this.elementRef.nativeElement;
    }
}
Share:
81,957
Kevin Regenrek
Author by

Kevin Regenrek

I'm a web developer from Austria interested in Serverless and Jamstack You can find me via twitter @kregenrek pinterest pinterest.com/regenrek

Updated on September 07, 2020

Comments

  • Kevin Regenrek
    Kevin Regenrek over 3 years

    there a some similiar threads but I couldn't find a suitable answer for my needs. So that direct DOM access should be strictly avoided in angular2 I'm just wondering whats best practice for this.

    What I wan't to achieve is a resize of a element based on the current width.

    workitemresize.component.ts

    import { Directive, ElementRef, HostListener, Renderer, Input } from '@angular/core';
    
    @Directive({
      selector: '[workItemResize]'
    })
    
    export class WorkItemResizeDirective implements ngAfterViewInit {
    
      private el: HTMLElement;
      private renderer: Renderer;
    
      constructor(el: ElementRef, public renderer: Renderer)
      { 
        this.el = el.nativeElement; 
        this.renderer = renderer;
      }  
    
    
      @HostListener('window:resize', ['$event.target']) 
      onResize() 
      { 
        this.resizeWorks();
      }
    
      ngAfterViewInit() {
        this.resizeWorks();
      }
    
      private resizeWorks(): void {
        this.renderer.setElementStyle(this.el, 'height', this.el.width); // <-- doesn't work I kow that this.el.width doesn't exist but just for demonstration purpose
        this.renderer.setElementStyle(this.el, 'height', '500'); // <-- works
      }
    
    }
    

    projects.template.html

    <div class="posts row">
            <div class="work-item col-xs-12 col-sm-6 col-no-padding"  workItemResize *ngFor="let post of posts">
    
                    <!-- show some fancy image -->
                    <div class="img"  [ngStyle]="{'background-image':'url('+post.better_featured_image.source_url+')'}"></div>
    
            </div>
    </div>
    

    Related: https://github.com/angular/angular/issues/6515

  • Kevin Regenrek
    Kevin Regenrek over 7 years
    Is there any notable difference between the @HostBinding and the Renderer approach? I think both versions are fine? Oh and this.el.nativeElement.width is always null... so I was struggling with this also. What about: this.renderer.setElementStyle(this.el, 'height', this.el.clientWidth+'px'); It works but looks odd to me...
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    The renderer approach is fine as well. I find the HostBinding approach easier to read. Your question has this.el.width instead of this.el.nativeElement.width. I assume resizeWorks is called before the component is fully rendered or otherwise try getBoundingClientRect().width.
  • Kevin Regenrek
    Kevin Regenrek over 7 years
    this.el references to el.nativeElement (see constructor). Ok I stick to the getBoundingClientRect().width. Thank you! The final Code line: this.renderer.setElementStyle(this.el, 'height', this.el.getBoundingClientRect().width+"px");
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    Right. Sorry, missed that one. So getBoundingClientRect() worked?
  • Kevin Regenrek
    Kevin Regenrek over 7 years
    Also a nice solution!
  • WebWanderer
    WebWanderer about 4 years
    This is very clever.
  • HugoPrunaux
    HugoPrunaux over 3 years
    This works but Angular's Change Detection will make the browser run the getBoundingClientRect method each time a change is detected. Unless you specifically want the style of that div to be updated this way, you should instead store that width in the .ts file, and update it only when needed.