Wrapping a third party component that changes DOM

Yesterday, I had a go at using textFit to autosize the text inside a div to fill it completely. The challenge was that textFit() changes the DOM, which makes the Aurelia databinding stop working. I ended up finding a solution, but it seems inelegant, and I can’t help thinking there must be a much simpler way to do this.

This is how I did it:

import { bindable, autoinject, TaskQueue, LogManager, noView, ViewFactory, ViewCompiler, ViewSlot, Container, ViewResources } from 'aurelia-framework';
import textFit from 'textfit';
import './song-part.scss'

const logger = LogManager.getLogger('song-part');

export class SongPart {
  viewSlot: ViewSlot;
  container: Container;
  viewFactory: ViewFactory;
  lineContainer: Element;
  taskQueue: TaskQueue;
  @bindable lines: any;

  constructor(taskQueue: TaskQueue, viewCompiler: ViewCompiler, viewSlot: ViewSlot, container: Container, viewResources: ViewResources) {
    this.taskQueue = taskQueue;
    this.viewSlot = viewSlot;
    this.container = container;
    this.viewFactory = viewCompiler.compile(
      + '  <div ref="lineContainer" class="line-container">'
      + '    <span repeat.for="line of lines">${line}<br if.bind="!$last"></span>'
      + '  </div>'
      + '</template>', viewResources);

  linesChanged(newValue: any) {
    logger.debug('linesChanged', newValue);
    let view = this.viewFactory.create(this.container);

    this.taskQueue.queueMicroTask(() => {
      textFit(this.lineContainer, { alignHoriz: true, alignVert: true });


.line-container {
  width: 100vw;
  height: 100vh;

Use custom attribute to bind the model, then manually control html.

<div class="line-container" text-fit.bind="lines"></div>


import {inject} from 'aurelia-framework';
import textFit from 'textfit';

export class TextFitCustomAttribute {
  constructor(element) {
    this.element = element;

  valueChanged(newValue, oldValue) {
    if (!this.live) return;

  _buildHtml(value) {
    if (value && value.join) {
      this.element.innerHTML = value.join('<br>');
    } else {
      this.element.innerHTML = value || '';
    textFit(this.element, { alignHoriz: true, alignVert: true });

  attached() {
    this.live = true;

  detached() {
    this.live = false;

You can go even fancier if you want to. Using dynamic-options-binding, you can support
text-fit="value: lines; options.bind: {alignHoriz: true, alignVert: true}".