javascriptcssangulartypescriptdom

Set select element width equal to it's option's content width dynamically


Im trying to make select element width equal to it's option text width in angular.

My HTML component simply look like this:

<form [formGroup]="profileForm">
  ...
  <select (change)="adjustSelectWidth($event.target)" formControlName="members">
    <option value="1">Me</option>
    <option value="2">Me, my spouse</option>
    <option value="3">Me, my spouse and my kids</option>
  </select>
  ...
</form>

What I have tried:

export class AppComponent  {
  profileForm = new FormGroup({
    members : new FormControl(''),
  })

  adjustSelectWidth(e){
    const optionValue = this.profileForm.get('members').value;
    const optionWidth = document.querySelector(`option[value="${optionValue}"]`).clientWidth;
    e.style.width= optionWidth + "px"
  }
}
export class AppComponent  {
  profileForm = new FormGroup({
    members : new FormControl(''),
  })

  adjustSelectWidth(e){
    const optionValue = this.profileForm.get('members').value;
    const optionTextLength = document.querySelector(`option[value="${optionValue}"]`).innerHTML.length;
    e.style.width= optionTextLength*8 + "px";
  }
}
export class AppComponent  {
  //This temp is bind to a span via string interpolation {{...}}
  temp:string;

  profileForm = new FormGroup({
    members : new FormControl(''),
  })

  adjustSelectWidth(e){
    const optionValue = this.profileForm.get('members').value;
    const optionText = document.querySelector(`option[value="${optionValue}"]`).innerHTML;
    this.temp = optionText;
    const spanWidth = document.querySelector(`.temp`).clientWidth;
    e.style.width = spanWidth + "px";
  }
}

Since im using angular, i prefer not to use JQuery. Additionally, why the clientWidth seem to not solve my problem here

I created a stackbliz example: https://stackblitz.com/edit/angular-bbimkz


Solution

  • There is a way to measure text width. I think it is sufficiently reliable, but might be a bit expensive in terms of performance, depending on your app of course. (The simplistic example in Stackblitz has no performance problem.)

    The method is to actually append a hidden element to the document containing the text you want to measure, read the clientWidth, and remove the element immediately.

    Modify adjustSelectWidth() as follows:

    adjustSelectWidth(e: HTMLSelectElement){
      // assuming something is always selected, please test!
      const displayedText = e.options[e.selectedIndex].innerText;
      const dummy = document.createElement('div');
      dummy.innerText = displayedText;
      dummy.style.position = 'absolute';
      dummy.style.visibility = 'hidden';
      document.body.insertBefore(dummy, document.body.firstChild);
      const measuredWidth = dummy.clientWidth;
      document.body.removeChild(dummy);
      e.style.width = (measuredWidth + 30) + 'px'
    }
    

    Modified Stackblitz: https://stackblitz.com/edit/angular-mpjxbd?file=src/app/app.component.ts - tested in latest Firefox, Chrome, Edge