javascripthtmlcssreactjswidth

Set element width based on a string that is not in the element


I have a React component that can render one string or another.

const MyComponent = (props: {
    readonly textA: string;
    readonly textB: string;
    readonly renderB: boolean;
}) => {
    return <>{props.renderB ? props.textB : props.textA}</>;
};

The problem is when I use this and I toggle renderB on and off, the width of the component can change based on which string is rendered. I want the component width to fit the longer of the two strings.

How can I take the max length of textA and textB and use that to determine the width of my component?

For example, it could look something like this:

const MyComponent = (props: {
    readonly textA: string;
    readonly textB: string;
    readonly renderB: boolean;
}) => {
    const widthA = /* compute the width of props.textA */;
    const widthB = /* compute the width of props.textB */;

    return (
        <div style={{ width: `${Math.max(widthA, widthB)}px` }}>
            {props.renderB ? props.textB : props.textA}
        </div>
    );
};

Solution

  • One solution is to use grid layout, render both child span, and hide the one that shouldn't be rendered (using visibility:hidden). The advantage is that this can be done pure CSS without needing to calculate the Text size, which is usually pretty troublesome and buggy. But the drawback is of course the user can see the other child in the DOM tree, which might not be ideal depending on your use case.

    .wrapper {
      display: inline-grid;
      border: 1px solid red; /* for showing the size*/
    }
    
    .wrapper>span {
      grid-column: 1;
      grid-row: 1;
      text-wrap: nowrap;
    }
    
    .wrapper>span:not(.rendered) {
      visibility: hidden;
    }
    <span class="wrapper"> 
      <span>Short Text</span>
      <span class="rendered">Example 1. Long text I'm very long</span>
    </span>
    <span class="wrapper"> 
      <span class="rendered">Example 2. Short Text</span>
      <span>Very Long textttttttttttttttttttttttttttttttttttt</span>
    </span>

    Code sample

    const MyComponent = (props: {
        readonly textA: string;
        readonly textB: string;
        readonly renderB: boolean;
    }) => {
        return (
            <span style={{ display: 'inline-grid' }}>
                <span
                    style={{
                        gridColumn: 1,
                        gridRow: 1,
                        visibility: props.renderB ? 'hidden' : 'visible',
                    }}
                >
                    {props.textA}
                </span>
                <span
                    style={{
                        gridColumn: 1,
                        gridRow: 1,
                        visibility: props.renderB ? 'visible' : 'hidden',
                    }}
                >
                    {props.textB}
                </span>
            </span>
        );
    };