javascripthtmlcssember.js

Ember.js component button may have one of a fixed listed of values, how to make it use minimum width for longest string?


Single button, it may display the text "Show", or "Hide".

Toggling between show and hide causes the width of the button to change.

I want the button to take up as much width as it needs to show the widest string irrespective of which string is displayed, so that it takes up as little width as needed but doesn't jiggle the display.

I've dug through various sites, my google-foo is not up to finding this answer.

Demonstrates width bounce



Ember code...

<div ...attributes>
{{#let (unique-id) as |passwordId|}}
<label class="block uppercase" for="{{passwordId}}">password</label>
<div class="flex flex-row">
    <div class="border-2 border-besler-blue p-2">
        <input class="outline-none" id="{{passwordId}}" type="{{if this.isHidden "password" "text"}}" value="{{@startValue}}" placeholder="{{@placeholder}}">
    </div>
    <div class="border-r-2 border-y-2 border-besler-blue p-2 bg-gray-200">
        <button type="button" {{on "click" this.toggleShow}}>{{if this.isHidden "Show" "Hide"}}</button>
    </div>
</div>
{{/let}}
</div>

EDIT UPDATE WITH ANSWER APPLIED The given answer is correct. Just two clarifications, the longest text needs to be the second item, and the style="position: relative" is not required.

<div ...attributes>
    {{#let (unique-id) as |passwordId|}}
    <label class="block uppercase" for="{{passwordId}}">password</label>
    <div class="flex flex-row">
        <div class="border-2 border-besler-blue p-2">
            <input class="outline-none" id="{{passwordId}}" type="{{if this.isHidden " password" "text" }}"
                value="{{@startValue}}" placeholder="{{@placeholder}}">
        </div>
        <button class="border-r-2 border-y-2 border-besler-blue p-2 bg-gray-200 text-center" type="button"
            {{on "click" this.toggleShow}}>
            <div>
                <span class="absolute {{if this.isHidden " opacity-0"}}">Hide</span>
                <span class="{{if (not this.isHidden) " opacity-0"}}">Show</span>
            </div>
        </button>
    </div>
    {{/let}}
</div>

Solution

  • You can do this with CSS by overlaying both texts on top of eachother and turning the inactive text invisible.

    I've made a runnable demo here, reproducing the original problem:

        <button {{on "click" this.increment}}>
          Click<br>
          {{#if (isEven this.count)}}
            This is Even
          {{else}}
            Odd
          {{/if}}
        </button>
    

    if we change these plain text values to be contained within elements that we can style, and change the position of:

        <button {{on "click" this.increment}}>
          Click<br>
          <div style="position: relative">
            <span style="position: absolute">Odd</span>  
            <span>This is Even</span>
          </div>
        </button>
    

    ends up looking like this enter image description here

    and then if we add back our show/hide logic:

        <button {{on "click" this.increment}}>
          Click<br>
          <div style="position: relative">
            <span style="position: absolute; {{if (isEven this.count) "opacity:0;"}}">Odd</span>  
            <span style={{if (isEven this.count) "" "opacity:0;"}}>This is Even</span>
          </div>
        </button>
    

    Play with it here

    looks like this: https://imgur.com/a/wLU83Dm

    enter image description here enter image description here