skip to Main Content

How is it possible to size a <div> not based on the real content, but based on a non-displayed ghost content?

Example: here the box with content "A" should be sized as if its content was "BCD":

.container { display: flex; width: 10em; }
.item { flex-grow: 1; text-align: center; padding: 0.5rem 1rem;
border: solid black 1px; }
<div class="container">
<div class="item" data-ghost-content="BCD">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>

Note: the container is a flexbox.

5

Answers


  1. Yes, you can make the additional content visibility: hidden; and it will consume space but not be shown:

    .container { display: flex; width: 10em; }
    .item { flex-grow: 1; text-align: center; padding: 0.5rem 1rem;
    border: solid black 1px; }
    [data-ghost-content]:after {
      content: attr(data-ghost-content);
      visibility: hidden;
    }
    <div class="container">
    <div class="item" data-ghost-content=BC>A</div>
    <div class="item">B</div>
    <div class="item">C</div>
    </div>

    In this approach, the "ghost content" would not include the actual visible content that’s already there in the element, so it’s just "BC".

    If you want the result to be centered, you could have "ghost" data attributes for ":before" and ":after".

    Login or Signup to reply.
  2. Not sure if you can adjust your structure a bit?

    .container { display: flex; width: 10em; }
    .item { flex-grow: 1; text-align: center; padding: 0.5rem 1rem;
    border: solid black 1px; }
    .ghost {
      visibility: hidden;
      height: 0;
    }
    <div class="container">
    <div class="item">
      <div class="item-content ghost">ABC</div>
      <div class="item-content">A</div>
    </div>
    <div class="item">B</div>
    <div class="item">C</div>
    </div>
    Login or Signup to reply.
  3. It depends on how your page is created, manually or with JS. But if you can manage to add an inline style with a --char-count custom property you can use the flex shorthand to get a generic solution:

    .item { flex: var(--char-count, 1) } /* or defaults to: 1 1 0% */
    

    Do not use flex-grow as for this solution flex-basis actually needs to be 0%

    The fun part of flexbox is that, when using different flex-grow values (as flex), the mechanism will distribute the available space to ratio using the various defined flex-grow values.

    BTW: In CSS var(.., fallback) the fallback value can also be 0 if so required, resulting in flexbox default flex-grow: 0 for ‘unset’ items.

    .container {
        display: flex;
        width: 10em;
    }
    .item {
        flex: var(--char-count, 1); /* fallback 1 if 'unset' */
        text-align: center;
        padding: 0.5rem 1rem;
        border: solid black 1px;
    }
    <div class="container">
        <div class="item" data-ghost-content="BCD" style="--char-count: 3">A</div>
        <div class="item">B</div>
        <div class="item">C</div>
    </div>
    Login or Signup to reply.
  4. How about adding a .wrapper, like this:

    <div class="container">
      <div class="item" data-ghost-content="BCD">
        <div class="wrapper">A</div>
      </div>
      <div class="item">
        <div class="wrapper">B</div>
      </div>
      <div class="item">
        <div class="wrapper">C</div>
      </div>
    </div>
    

    …and then, a hidden pseudo-element containing the ghost content. The actual content will be absolute-ly positioned so as not to consume any space:

    [data-ghost-content]::before {
      content: attr(data-ghost-content);
      visibility: hidden;
    }
    
    [data-ghost-content] > .wrapper {
      position: absolute;
    }
    

    To preserve text-aligning, make .item flexboxes:

    .item {
      display: flex;
      justify-content: center;
    }
    

    Try it:

    .item {
      display: flex;
      justify-content: center;
    }
    
    [data-ghost-content]::before {
      content: attr(data-ghost-content);
      visibility: hidden;
    }
    
    [data-ghost-content] > .wrapper {
      position: absolute;
    }
    
    
    /* Demo only */
    
    .container {
      display: flex;
      width: 10em;
    }
    
    .item {
      flex-grow: 1;
      text-align: center;
      padding: 0.5rem 1rem;
      border: solid black 1px;
    }
    <div class="container">
      <div class="item">
        <div class="wrapper">E</div>
      </div>
      <div class="item" data-ghost-content="BCD">
        <div class="wrapper">A</div>
      </div>
      <div class="item">
        <div class="wrapper">B</div>
      </div>
      <div class="item">
        <div class="wrapper">C</div>
      </div>
    </div>
    Login or Signup to reply.
  5. On the assumption that you’re able to adjust the HTML, to include a <span> tag for the visible content, then I’d suggest using CSS grid, as this allows elements to be stacked "on top of" each other, in the space from background to foreground, as follows (with explanatory comments in the code):

    .container {
      display: flex;
      width: 10em;
    }
    
    .item {
      flex-grow: 1;
      text-align: center;
      padding: 0.5rem 1rem;
      border: solid black 1px;
    }
    
    /* setting up CSS grid on .item elements with the
       data-ghost-content attribute: */
    .item[data-ghost-content] {
      display: grid;
      /* placing items to the start (on the block-axis, vertical
         in English) and to the center (on the inline-axis,
         horizontal in English); this is in the event of a longer
         "data-ghost-content" attribute being set so the visible
         content is placed similarly to the neighbouring
         elements/content: */
      place-items: start center;
    }
    
    .item[data-ghost-content] > span {
      /* placing the <span> in the first column and first row: */
      grid-column: 1;
      grid-row: 1;
    }
    
    /* using the ::before pseudo-element to display the
       attribute-value of the data-ghost-content attribute: */
    .item[data-ghost-content]::before {
      content: attr(data-ghost-content);
      /* placing the pseudo-element in the first row and column
         of the grid, similarly to the <span> element: */
      grid-column: 1;
      grid-row: 1;
      user-select: none;
      /* hiding the element by setting its visibility to hidden,
         which allows it to retain its space in the document: */
      visibility: hidden;
      /* positioning the pseudo-element behind the <span>: */
      z-index: -1;
    }
    <div class="container">
      <div class="item" data-ghost-content="BCD">
        <span>A</span>
      </div>
      <div class="item">
        <span>B</span>
      </div>
      <div class="item">
        <span>C</span>
      </div>
    </div>

    JS Fiddle demo.

    References:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search