Binding to Element Size

Basic implementation

import { bindable, BindingMode, customAttribute } from 'aurelia';

@customAttribute({
  name: 'rectsize',
  hasDynamicOptions: true
})
export class RectSize {

  public static inject = [INode];

  @bindable({ mode: BindingMode.fromView })
  public value!: ResizeObserverSize;

  @bindable()
  public boxSize!: 'border-box' | 'content-box';

  private observer!: ResizeObserver;

  constructor(
    private readonly element: HTMLElement
  ) {

  }

  public binding(): void {
    let observer = this.observer;
    if (observer === void 0) {
      observer = this.observer = this.createObserver();
    }
    observer.observe(this.element, { box: 'border-box' });
  }

  public unbinding(): void {
    this.observer.disconnect();
    this.observer = (void 0)!;
  }

  private createObserver(): ResizeObserver {
    return new ResizeObserver((entries) => {
      this.handleResize(entries[0]);
    });
  }

  /**
   * @internal
   */
  private handleResize(entry: ResizeObserverEntry): void {
    this.value = this.boxSize === 'content-box' ? entry.contentBoxSize : entry.borderBoxSize;
  }
}

Working with polyfill

As ResizeObserver is still new and experimental API, it's not widely supported in all browsers. Fortunately there are a few polyfills available. For example:

To make the attribute work seamlessly with any polyfill users want to choose, we can adjust the way we get the ResizeObserver constructor, as shown in the following example:

export class RectSize {

+  /**
+   * Allow user to ponyfill Resize observer via this static field
+   */
+  public static ResizeObserver: ResizeObserver;


+  private createObserver(): ResizeObserver {
+    const ResizeObserverCtor = this.getResizeObserver();
-    return new ResizeObserver((entries) => {
+    return new ResizeObserverCtor((entries) => {
+      this.handleResize(entries[0]);
+    });
+  }


+  private getResizeObserver(): ResizeObserver {
+    return RectSize.ResizeObserver || window.ResizeObserver;
+  }

And now, our user can switch to any polyfill as the following example:

import { RectSize } from './some-path'
import { ResizeObserver } from 'some-resize-observer-polyfill'

RectSize.ResizeObserver = ResizeObserver;

Example usage

For the above implementation, the usage would be:

<form rectsize.bind="formSize">
  ...
</form>
or
<form rectsize="value.bind: formSize; box-model: content-box;">
  ...
</form>

Special note:

For the polyfill at https://github.com/que-etc/resize-observer-polyfill , you cannot use box-model for observation option, as it follows an older spec of ResizeObserver. Though it is a mature polyfill, and works well so if you want to use it, slightly modify the above implementation:

  private handleResize(entry: ResizeObserverEntry): void {
-    this.value = this.boxSize === 'content-box' ? entry.contentBoxSize : entry.borderBoxSize;
+    this.value = entry.contentRect;
  }

Last updated