Reputation: 1561
I know you can use ngStyle to set styles of an element. But how do I get styles of an element (ie, the default styles as rendered by the browser)?
This code doesn't work but expresses what I'm trying to do:
<p>TEXT</p>
width of TEXT element is {{textElement.width}}
height of TEXT element is {{textElement.height}}
I want the p element to render normally (however the browser wants to, without telling it a width or height). However, I want to know what the rendered width and height are. How do I bind things to capture this?
EDIT: Some people asked what I want to do with the width and height. What I want to do is something like this:
<p *ngIf="!isEditing">{{myText}}</p>
<textarea *ngIf="isEditing" [(ngModel)]="myText"></textarea>
By default, p is displayed and textarea is not. If the user enters editing mode, p gets replaced by textarea. I wanted to set textarea's width and height to match p's so the transition looks seamless. Is there a better way to do this?
Upvotes: 13
Views: 39259
Reputation: 2623
this can also be done using @ViewChild
stackblitz link: https://stackblitz.com/edit/angular-playground-jzgein
component.ts:
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
@Component({
selector: 'my-app',
styleUrls: ['./app.component.scss'],
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
@ViewChild('viewChildHook', {static: true}) viewChildHook: ElementRef;
ngOnInit(){
const value = getComputedStyle(this.viewChildHook.nativeElement).getPropertyValue('--my-variable-name');
console.log("value", value);
}
}
component.html:
<div #viewChildHook class="html-dom-element">
aaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccc
</div>
component.scss:
.html-dom-element{
--my-variable-name: 300px;
width: var(--my-variable-name);
}
Upvotes: 11
Reputation: 105439
The best way is to use a directive through which you can get access to the native DOM element to which this directive is applied. Then you can wait until the DOM is rendered in AfterViewInit
hook and use getComputedStyle method to get all computed styles.
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[style-getter]'
})
export class StyleGetter {
constructor(public el: ElementRef) {}
ngOnChanges() {
setTimeout(() => {
console.log(this.el.nativeElement.offsetWidth);
})
}
ngAfterViewInit() {
console.log(this.el.nativeElement.offsetWidth);
}
}
You also should probably hook into ngOnChanges
hook since often DOM is re-rendered when input bindings change. You can use setTimeout to wait until all styles are computed and DOM is rendered.
You also have to use offsetWidth
instead of width
, because:
offsetWidth returns computed element's width, while el.style.width just returns width property defined in element.style by javascript and does not reflect real element's dimensions.
Read more here.
Upvotes: 8
Reputation:
The best way is not to do this.
The browser does layout for a living, driven by the HTML and the CSS. Together, they provide a rich set of capabilities for controlling layout, including media queries, flexbox, and many others.
Accessing an element's height and then adjusting this thing over there or moving this thing over there is a slippery slope. Before you know it, you will end up recreating, and redoing, much of the layout work that the browser is designed to do for you.
If you really want to get the height, there are of course ways to do that, as mentioned in other answers. However, before you start down this road, you should make sure that you really want to, and need to.
If you provide a more specific example of what you intend to do with the height once you retrieve it, people here can probably suggest lots of ways to accomplish the same thing without lots of Angular/TS code.
To accomplish what you want with the textarea replacing the <p>
, you could try this:
let editing = false;
function toggle() {
document.getElementById('div').classList.toggle('editing', editing = !editing);
}
div { border: 1px solid gray; position: relative; }
textarea {
display: none;
height: 100%; width: 100%;
position: absolute; top: 0; left: 0;
}
.editing textarea { display: block; }
.editing p { visibility: hidden; }
<p><button onclick="toggle()">Toggle editing</button></p>
<div id="div">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<textarea></textarea>
</div>
As I'm sure you can figure out by looking at the code, the approach is to put both the p
and the textarea
within a div
. The size of the div
will be governed by the size of the p
. The textarea
is positioned absolutely to align exactly with the div
. When in editing mode, the p
is set to visibility: hidden;
, which maintains its position/size in the layout, but hides it, and the textarea
is set to display: block;
so it is shown.
Adapted to Angular, and using the same CSS, this would be:
// component
editable = false;
// template
<p><button (click)="editable = !editable">Toggle editing</button></p>
<div id="div" [class.editable]="editable">
<p>{{myText}}</p>
<textarea [(ngModel)]="myText"></textarea>
</div>
Upvotes: -1