如何在Angular中获取DOM元素的样式?

11

我知道你可以使用ngStyle来设置元素的样式。但是,我如何获取元素的样式(即浏览器呈现的默认样式)?

这段代码不起作用,但表达了我想要做的事情:

<p>TEXT</p>
width of TEXT element is {{textElement.width}}
height of TEXT element is {{textElement.height}}

我希望p元素能够像普通情况下一样渲染(由浏览器自己决定,不指定宽度或高度)。然而,我想知道渲染后的宽度和高度是多少。我该如何绑定来捕获这些信息?

编辑:有些人问我想用宽度和高度做什么。我想做的事情类似于这样:

<p *ngIf="!isEditing">{{myText}}</p>
<textarea *ngIf="isEditing" [(ngModel)]="myText"></textarea>

默认情况下,显示 p 而不显示 textarea。如果用户进入编辑模式,则会将 p 替换为 textarea。我希望设置 textarea 的宽度和高度与 p 相匹配,以使过渡看起来无缝。是否有更好的方法来实现这一点?


你打算在获取宽度和高度后做什么? - user663031
尝试像这样做... angular.element(document.querySelector('#lilogin')).removeClass('active'); - geminiousgoel
3个回答

10

这也可以使用@ViewChild来完成

Stackblitz链接: 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);
}

8

最好的方法是使用指令,通过该指令可以访问应用此指令的本地DOM元素。然后你可以等待DOM在AfterViewInit钩子中呈现,并使用getComputedStyle方法获取所有计算样式。

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);
  }
}

你还应该钩入ngOnChanges钩子,因为当输入绑定发生变化时,DOM通常会重新渲染。你可以使用setTimeout等待直到所有样式都被计算并且DOM已经被渲染。
此外,你需要使用offsetWidth代替width,因为:offsetWidth返回计算后的元素宽度,而el.style.width仅返回由javascript在element.style中定义的width属性,不反映真实元素的尺寸。
更多信息请阅读这里

你好!你是不是想说 ngOnChanges 钩子函数? - yurzui
嗨,@MaxKoretskyiakaWizard,应该用this.el.nativeElement.offsetWidth代替styles.offsetWidth吧? - Andrei Gătej
@AndreiGătej,为什么? - Max Koretskyi
offsetWidth seems to be a property of HTMLElement, but not of CSSDeclarationStyles.(I might have misspelled the last one). Anyway, it worked for me using this.el.nativeElement.offsetWidth - Andrei Gătej
getComputedStyle导致所有DOM节点的样式重新计算,是一个重大的性能问题。 - Canttouchit

-1

最好的方法是不要这样做。

浏览器以 HTML 和 CSS 为驱动进行布局。它们共同提供了丰富的能力来控制布局,包括媒体查询、flexbox 等等。

访问元素的高度,然后调整那里的东西或移动那里的东西是一条很滑的坡。在你意识到之前,你将不得不重新创建和重做浏览器为你设计的大部分布局工作。

如果你真的想获取高度,当然有办法做到,就像其他答案中提到的那样。但是,在你开始这条路之前,你应该确保你真的想要,并且需要这样做。

如果您提供一个更具体的例子,说明您打算检索高度后要做什么,这里的人们可能会建议许多方法来完成相同的事情,而不需要大量的 Angular/TS 代码。

要实现您想要的文本区域替换 <p>,您可以尝试这样做:

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>

我相信你看代码应该能够理解这种方法,就是将ptextarea放在同一个div中,div的大小将由p的大小决定。textarea被绝对定位,以与div完全对齐。在编辑模式下,p被设置为visibility: hidden;,这样它仍然占据原来的位置/大小,但被隐藏了,而textarea则被设置为display: block;,所以它会显示出来。

适用于Angular,并使用相同的CSS,这将是:

// 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>

我已经编辑了描述我想要做的事情。除了访问宽度和高度之外,有更好的方法吗? - Ray Zhang
这很有帮助,但我遇到了一个问题。我还想将文本排列成多列格式,例如通过在div的样式中添加“column-width: 8em”。文本区域应该遵循相同的行为并排列成列。然而,目前当切换到textarea时,它只是覆盖整个div,完全不考虑列布局。如何使textarea符合列布局? - Ray Zhang
@RayZhang 我认为无论你如何操作,都很难甚至不可能让文本区域自动排列成列。这就是文本区域的工作方式。在这种情况下,你可能想尝试使用 contentEditable 进行实验。 - user663031

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接