【问题标题】:Element height and width change detection in Angular 2Angular 2中的元素高度和宽度变化检测
【发布时间】:2017-04-01 05:49:27
【问题描述】:

我一直在寻找解决方案,但什么也没找到(只有关于 (window:resize) 的文章,但不是我要寻找的)。

如何检测Angular 2中的元素大小变化?

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

我想使用一些CSS 动画并检测元素的heightwidth 变化。

【问题讨论】:

  • 您解决了吗先生?因为我也想要同样的东西。

标签: angular


【解决方案1】:

编辑:现代答案

现代浏览器支持ResizeObserver API。 API 已经推出多年,现在得到了广泛的支持。不幸的是,Edge 现在只支持它,因为它在 Chromium 上。如果这对您很重要,请查看旧答案或use a polyfill

如果你有兴趣,那就去吧。您想创建一个ResizeObserver 对象并在其上调用observe。在这个例子中,蓝色块在我们循环文本和填充时不断调整大小。元素的当前大小被添加到无序列表中。

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>

原答案

这个问题甚至不是 Angular 问题。更一般地说,您如何检测除window 之外的任何元素的大小变化?有一个onresize 事件,但这只是针对window 触发的,没有其他明显的解决方案。

许多解决此问题的一般方法是设置间隔,例如 100 毫秒,并检查 div 的宽度和高度以检测变化。听起来很可怕,但这是最常见的方法。

this answer 到更一般的问题,有一个库可以仅使用事件来执行此操作:http://marcj.github.io/css-element-queries/。想必,还算不错。您可以使用ResizeSensor 来获取您要查找的内容。

当然,除非您只希望 div 在窗口调整大小时调整大小。那么onresize 就是您要查找的内容。

【讨论】:

  • 有任何更新吗?使用 Angular 2-6 1.5 年后是否有更好的解决方案?
  • 老实说@tom 从那以后我就再也没有调查过。我不知道有任何新的浏览器 API,但即使存在这样的东西,你仍然会不得不支持 IE11(它比 Edge 更流行并且根本没有更新)。
  • @tom 我认为下面 Sufiyan Ansari 的解决方案是一个更新更好的解决方案。
  • 现在有更好的解决方案:web.dev/resize-observer
  • 感谢@JamesHollyer 的提醒。我想更新这个有一段时间了。
【解决方案2】:

检测角度分量的任何元素的变化。我们可以使用 ResizeObserver(来自 import ResizeObserver from 'resize-observer-polyfill'; 的类)无需库

这是我的实现:

进口:

import ResizeObserver from 'resize-observer-polyfill';

实施:

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

【讨论】:

  • 如果您观察自定义 Angular 元素(例如:组件的宿主元素),请不要忘记为其添加“显示:块”样式。因为它默认为“内联”并且没有正确的宽度/高度值。
【解决方案3】:

对于有角度的用户, 您可以轻松应用此代码....

HTML

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

角度

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

请记住,您必须安装 node 模块并将其导入到您的 app.module.ts 文件中。

查看此链接了解更多信息: https://www.npmjs.com/package/angular-resize-event

【讨论】:

    【解决方案4】:

    必须检测两种情况:

    1. 元素被修改
    2. 调整窗口大小

    由于 Angular 如果经常修改元素(添加类等),重要的是要等到更改“完成”。 Observables 可以用于此。

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

    JS 代码:

    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 代码

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

    【讨论】:

    • 您的代码只有在窗口被调整大小的情况下才能工作。什么用户正在寻找组件调整大小,这不仅可能在窗口调整大小时发生,而且在组件完全加载后也会发生。例如,网格组件初始高度为 100 像素,同时从后端加载行,加载后大小增加到 400 像素。在这种情况下,窗口调整大小不会发生,只有 div/组件高度会发生变化。这里 ResizeObeserver 是不错的选择。
    【解决方案5】:

    一个非常优雅的解决方案,类似于使用 RxJS 的最佳答案...

    HTML

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

    组件或指令

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

    【讨论】:

      猜你喜欢
      • 2012-05-31
      • 1970-01-01
      • 2015-01-21
      • 2016-10-04
      • 1970-01-01
      • 2017-10-02
      • 2019-12-29
      • 1970-01-01
      • 2012-10-15
      相关资源
      最近更新 更多