0

Why are "Foo" and "Bar" not aligned vertically in my example?

If I stop hiding the overflow from the parent of "Bar" (click the button) - suddenly the overflowed element "Baz" does align with "Foo".

When overflowed, "Foo" and "Bar" have the exact same height when inspected, they are simply rendered at a different height and I can't figure out why.

The JS is not relevant, just helps demonstrate the problem.

const target = document.querySelector("#second")
const button = document.querySelector("button")

toggle = true
button.addEventListener("click", () => {
  if (toggle) {
    target.style.overflow = "initial"
  } else {
    target.style.overflow = "hidden"
  }
  toggle = !toggle
})
* {
  margin: 0;
  padding: 0;
  line-height: 1em;
  box-sizing: border-box;
}
main {
  background: #eee;
  padding: 1em;
  font-size: 8vw;
}
#first {
  background: red;
  display:inline-block;
  height: 1em;
}
#second {
  background: blue;
  display:inline-block;
  height: 1em;
  overflow: hidden;
}
<button>Toggle overflow for #second</button>
<main>
  <span id="first">
    FOO[
  </span><span id="second">
    <div>]BAR</div>
    <div>]BAZ</div>
  </span>  
</main>
Joel Peltonen
  • 13,025
  • 6
  • 64
  • 100
  • 1
    As you're using `inline-block` display on those elements, you may consider applying a `vertical-align: middle` on them as well. Anyway, using `flexbox` could be a straightforward way to acheive your alignment goal. – ThS Feb 23 '23 at 14:13
  • 1
    I think it's because the span elements are trying to align along the same baseline. Hiding the overflow is breaking it as the baseline is at the bottom of the hidden text. – Adam Feb 23 '23 at 14:24
  • I don't understand the point but I guess you are trying to switch the bar with baz. But why manipulate the overflow if you can just switch the items? – Ricardo Silva Feb 23 '23 at 14:24
  • 1
    @RicardoSilva The original goal was to actually animate the change; `#second` would be cycling through options by animating `top`. See https://codepen.io/req-the-sasster/pen/eYLzvxa for finished one with a hardcoded hack to fix the alignment – Joel Peltonen Feb 23 '23 at 15:09
  • @ths and @Adam right you are! I added valign middle to both containers and that fixes it. Maybe add as answer and I'll accept? I would love to grok how the baseline actually works, I actually thought setting `line-height: 1em` and element height to `1em` would have stopped the baseline from moving. – Joel Peltonen Feb 23 '23 at 15:12
  • It's no worries dude. I'm glad you fixed it. I'm just having a go here :-£ https://codepen.io/adamuk73/pen/OJoXxxo – Adam Feb 23 '23 at 15:23

2 Answers2

1

Those are inline-blocks which by default are aligned along their baseline. Add vertical-align: top; and you'll be set:

const target = document.querySelector("#second")
const button = document.querySelector("button")

toggle = true
button.addEventListener("click", () => {
  if (toggle) {
    target.style.overflow = "initial"
  } else {
    target.style.overflow = "hidden"
  }
  toggle = !toggle
})
* {
  margin: 0;
  padding: 0;
  line-height: 1em;
  box-sizing: border-box;
}
main {
  background: #eee;
  padding: 1em;
  font-size: 8vw;
}
#first {
  background: red;
  display:inline-block;
  height: 1em;
}
#second {
  background: blue;
  display:inline-block;
  height: 1em;
  overflow: hidden;
  vertical-align: top;
}
<button>Toggle overflow for #second</button>
<main>
  <span id="first">
    FOO[
  </span><span id="second">
    <div>]BAR</div>
    <div>]BAZ</div>
  </span>  
</main>
Johannes
  • 64,305
  • 18
  • 73
  • 130
  • `Those are inline-blocks which by default are aligned along their baseline` Sure, but why are their baselines different I guess is my question :) They have identical height and line-height, so I would assume that they are vertically indentical. Not sure why my Q was marked as a dupe, I certainly don't think it's the same question. – Joel Peltonen Feb 24 '23 at 15:51
  • 1
    The `#second` element has more than one line, also if the second line is hidden by the `overflow:hidden` setting, but because of that it is aligned along its bottom border, along the baseline of the first element. If you remove the `overflow:hidden` setting, the elements are aligned along their "lowest" baseline. If you activate `overflow:hidden` again and gradually change the `height`setting of `#second`, you'll see that it's always the bottom border of `#second` that is aligned with the baseline of `#first` – Johannes Feb 24 '23 at 16:02
  • 1
    Fantastic, thank you so much for the explanation! Baseline behavior seems strange to me. I know my approach is alltogether suboptimal and inline elements aren't meant for what I'm doing in any case. Thank you again. – Joel Peltonen Feb 24 '23 at 20:26
0

Ok, so looking at your example. I think you can solve this with flex containers. for sure you can cleanup this a bit. But this removes the need to correct the alignment with margin.

* {
  margin: 0;
  padding: 0;
  line-height: 1em;
}

/* To help understand what is going on, uncomment below - note that this does slighty break the animation */
/*
{ border: 0.1vw solid red; }
*/

body {
  display: grid;
  place-items: center;
  margin: 0;
  height: 80vh;
  background: lightpink;
}

.container {
  background: white;
  font-size: 5vw; /* This determines the main size! */
  padding: 1em;
  display: flex;
}
.before {
  display: flex;
  height: 1.5em;
  align-items: center;
}
.animate {
  display: flex;
  flex-direction: column;
  height: 1.5em;
  overflow: hidden;
  position: relative;

}
.animate span {
  background:lightpink;
  display: flex;
  flex-direction: column;
  position: relative;
  line-height: 1.5em;
  animation: scroll 6s infinite linear;
}
@keyframes scroll {
  /* Found with experimentation, not math */
  0% { top: -4.48em } 
  100% { top: -9em; }
}

Here a codepen

Ricardo Silva
  • 1,221
  • 9
  • 19