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

Posted

技术标签:

【中文标题】如何在 Angular 中获取 DOM 元素的样式?【英文标题】:How to get style of DOM elements in Angular? 【发布时间】:2017-10-19 02:39:36 【问题描述】:

我知道您可以使用 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 匹配,以便过渡看起来无缝。有没有更好的方法来做到这一点?

【问题讨论】:

得到宽度和高度后你打算怎么处理? Angular 2:How to get element's width/height within directives and component?的可能重复 试试这样的...... angular.element(document.querySelector('#lilogin')).removeClass('active'); 【参考方案1】:

最好的方法是使用一个指令,通过它您可以访问应用该指令的本机 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 属性和 不反映真实元素的尺寸。

阅读更多here。

【讨论】:

嗨!你是说ngOnChanges钩子吗? 嗨@MaxKoretskyiakaWizard,不应该有this.el.nativeElement.offsetWidth 而不是styles.offsetWidth 吗? @AndreiGătej,为什么? offsetWidth 似乎是 htmlElement 的属性,而不是 CSSDeclarationStyles 的属性。(我可能拼错了最后一个)。无论如何,它对我有用this.el.nativeElement.offsetWidth getComputedStyle 导致所有 DOM 节点上的样式重新计算,主要性能问题【参考方案2】:

这也可以使用@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);

【讨论】:

【参考方案3】:

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

浏览器以布局为生,由 HTML 和 CSS 驱动。它们一起提供了一组丰富的控制布局的功能,包括媒体查询、flexbox 和许多其他功能。

访问一个元素的高度,然后在那里调整这个东西或者把这个东西移动到那里是一个滑坡。在不知不觉中,您最终将重新创建和重做浏览器旨在为您完成的大部分布局工作。

如果你真的想获得高度,当然有办法做到这一点,如其他答案所述。但是,在您开始这条路之前,您应该确保您确实想要并且需要这样做。

如果你提供一个更具体的例子来说明你在检索高度后打算如何处理它,这里的人们可能会建议很多方法来完成同样的事情,而无需大量的 Angular/TS 代码。

要使用 textarea 替换 &lt;p&gt; 来完成你想要的,你可以试试这个:

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>

【讨论】:

我已经编辑来描述我正在尝试做的事情。有没有比访问宽度和高度更好的方法? 这很有帮助,但我遇到了问题。我还希望文本以多列格式排列,即。例如在 div 的样式中添加“column-width: 8em”。文本区域应遵循与文本相同的行为并排列成列。但是,当前文本区域在打开时仅覆盖整个 div,根本不考虑列布局。如何使 textarea 符合列布局? @RayZhang 我认为无论您如何操作,都很难让 textarea 按列排列。这不是 textareas 的工作方式。在这种情况下,您可能想尝试使用 contentEditable

以上是关于如何在 Angular 中获取 DOM 元素的样式?的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS操作DOM——angular.element

如何使用 Angular 4 从 Parent 获取子 DOM 元素引用

如何通过选择器访问 DOM 元素

在 Angular JS 模块中传递并获取 DOM 元素值

如何在 Angular 指令的 Link 函数中设置 CSS 样式

Angular使用总结 --- 如何正确的操作DOM