I do not “get” the vertical-align css property

2019-02-25 06:16发布

问题:

I do not understand when vertical-align will and won't work.

Every time I run into a use case for vertical-align it seems to be a coin toss as to whether it will actually work. I know it has to be applied to inline elements. I have read that I must specify a line-height for elements that do not normally have one. I have read that the height property must have a static (non-auto/non-%) value. I have read that some (modern) browsers do not handle vertical-align correctly if the element they are used on is not naturally an inline element. I am unclear on whether vertical-align should be on the containing element (like text-align) or the element I want vertically aligned.

I've created this jsfiddle to try to work out the problem, but remain confused.

In the jsfiddle above I would expect #header to be centered halfway between the top and bottom of #outer and #inner. Obviously, that's not the case.

回答1:

Simply said: vertical-align is only active/valid when the element it is applied to has display: inline-block or ìnline, which for example is useful if you want to align a bunch of images at their top border: You define them as inline-blocks and apply vertical-align: top to them

Here is an example:

.wrapper {
  height: 250px;
  border: 1px solid green;
}

.wrapper>img {
  display: inline-block;
  vertical-align: top;
}
<div class="wrapper">
  <img src="https://placehold.it/120x40">
  <img src="https://placehold.it/70x140">
  <img src="https://placehold.it/80x60">
  <img src="https://placehold.it/60x140">
</div>

In your fiddle, the elements are nested into each other, not next to each other, i.e. they are not siblings - there is only one child element each, so there is no alignment of siblings like in the example above.



回答2:

The easiest way to understand why it's not working it to add many line of text because vertical-align will align on the respective line (the line-box) and not the whole container like you may think.

So if we add more text we have the following.

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  width:180px;
  display: inline-block;
  overflow:hidden;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Some Text Some Text Some Text
    </h1>
  </div>
  <div id="inner">
    <h1 class="header" style="vertical-align:middle;">
      Some Text Some Text Some Text
    </h1>
  </div>
  <div id="inner">
    <h1 class="header" style="vertical-align:top;">
      Some Text Some Text Some Text
    </h1>
  </div>
</div>

Basically nothing to align and all the alignments are almost equivalent simply because the text is defining the line-box thus its height is equal to the height of the line box and there is no room for alignment.

Now let's increase the height of the line (using line-height)

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  width:180px;
  line-height:200px;
  display: inline-block;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
  line-height:1em;
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Some Text Some Text Some Text
    </h1>
  </div>
  <div id="inner">
    <h1 class="header" style="vertical-align:middle;">
      Some Text Some Text Some Text
    </h1>
  </div>
  <div id="inner">
    <h1 class="header" style="vertical-align:top;">
      Some Text Some Text Some Text
    </h1>
  </div>
</div>

See how each line is now bigger and each text is aligned on its respective line that has a height of 200px and we can clearly see how the alignment are different.

In this case, the text has enough room to be aligned like you want and we see the magic if we keep only one line of text:

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  line-height: 200px;
  display: inline-block;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
  line-height:1em;
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Some Text
    </h1>
    <h1 class="header" style="vertical-align:middle">
      Some Text
    </h1>
    <h1 class="header" style="vertical-align:top">
      Some Text
    </h1>
    <h1 class="header" style="vertical-align:bottom">
      Some Text
    </h1>
  </div>
</div>

You may also note how middle and baseline are very close because:

Aligns the middle of the element with the baseline plus half the x-height of the parentref

There is only a half x-height of difference.

Another important fact to note is that if we don't set a line height to the inline element this one will inherit the line-height of his parent and alignment like top won't have any effect.

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  line-height: 200px;
  display: inline-block;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Some Text
    </h1>
    <h1 class="header" style="vertical-align:middle">
      Some Text
    </h1>
    <h1 class="header" style="vertical-align:top">
      Some Text
    </h1>
  </div>
</div>

Aligns the top of the element and its descendants with the top of the entire line.

Having the same line-height means that the element is already at the top and also the bottom of the line box so both top bottom will behave like baseline (the default value).


The height of the line-box can also be controled by the height of elements. You may have a big font-size for one element that will make the height of the line bigger and have enough room to align small texts next to it in the same line box:

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  display: inline-block;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
}
@keyframes change {
  from {font-size:20px;}
  to {font-size:100px;}
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Text
    </h1>
    <h1 class="header" style="font-size:100px;animation:change 5s linear alternate infinite">
      T
    </h1>
    <h1 class="header" style="vertical-align:middle;">
      Text
    </h1>
    <h1 class="header" style="vertical-align:top;">
      Text
    </h1>
    <h1 class="header" style="vertical-align:bottom;">
      Text
    </h1>
  </div>
</div>

You can also constrol the line box by setting height of inline-block element:

#outer {
  box-sizing: border-box;
  border: red 1px solid;
  height: 200px;
  text-align: center;
}

#inner {
  border: blue 1px solid;
  height: 200px;
  display: inline-block;
}

.header {
  display: inline;
  border: green 1px solid;
  margin: 0;
}
.elem {
 display:inline-block;
 background:red;
 width:2px;
 height:5px;
 animation:change 5s linear alternate infinite;
}
@keyframes change {
  from {height:20px;}
  to {height:100px;}
}
<div id="outer">
  <div id="inner">
    <h1 class="header">
      Text
    </h1>
    <div class="elem">
    </div>
    <h1 class="header" style="vertical-align:middle;">
      Text
    </h1>
    <h1 class="header" style="vertical-align:top;">
      Text
    </h1>
    <h1 class="header" style="vertical-align:bottom;">
      Text
    </h1>
  </div>
</div>

As a conclusion: to align using vertical-align you should have a line box that has a height bigger enough (explicitely set or set by other elements) where you can align your element. If your element is defining the line-box (which is the common case) then there is nothing to align.


Some good questions to have more detais:

Vertical align not working on inline-block

Inline elements and line-height

My inline-block elements are not lining up properly

Why is this inline-block element pushed downward?