Search code examples
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:

  • Get the option element width using clientWidth then apply that to select element but that width is 0.
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"
  }
}
  • Get the option's innerHTML length then mutiply it with a fixed pixel but it not a dynamic option
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";
  }
}
  • Append the options's innerHTML to a span element for measuring width, but then that span clientWidth does not accurate when I apply it's to the select element
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