ChatGPT解决这个技术问题 Extra ChatGPT

How to set the margin or padding as percentage of height of parent container?

I had been racking my brains over creating a vertical alignment in css using the following

.base{ background-color:green; width:200px; height:200px; overflow:auto; position:relative; } .vert-align{ padding-top:50%; height:50%; }

Content Here

While this seemed to work for this case, i was surprised that when i increased or decreased the width of my base div, the vertical alignment would snap. I was expecting that when I set the padding-top property, it would take the padding as a percentage of the height of the parent container, which is base in our case, but the above value of 50 percent is calculated as a percentage of the width. :(

Is there a way to set the padding and/or margin as a percentage of the height, without resorting to using JavaScript?


T
TylerH

The fix is that yes, vertical padding and margin are relative to width, but top and bottom aren't.

So just place a div inside another, and in the inner div, use something like top:50% (remember position matters if it still doesn't work)


top works great for resizing based on browser/window height. How aboutt having a div underneath that div? How can you clear the 2nd div to be under the div that is shifted down by a top:50%??
Learn sth new. Did not know vertical padding and margin are relative to width before. Thank you.
Also possible to add a 'spacer' div with a set percentage height. Seems little dirty but it works. see this answer.
What the everliving @%!^? Why are vertical margin and padding relative to width? WHAT? Why on earth was that decision made?
Your answer is extremely disproportionately up-voted compared to alain's answer below, which gives a general solution, and I found it very helpful and almost didn't see it. top: 50% is not very useful if you need to align the contents by their own center.
w
willoller

An answer to a slightly different question: You can use vh units to pad elements to the center of the viewport:

.centerme {
    margin-top: 50vh;
    background: red;
}

<div class="centerme">middle</div>

Why isn't this answer voted higher? Is there something wrong with using vh?
It's because it's not an answer that is tailored to the original question, it's a useful trick that works only in a few related cases.
using vh is almost the same as percentages... even worse in some cases. This answer is irrelevant to the question
u
user

Here are two options to emulate the needed behavior. Not a general solution, but may help in some cases. The vertical spacing here is calculated on the basis of the size of the outer element, not its parent, but this size itself can be relative to the parent and this way the spacing will be relative too.

<div id="outer">
    <div id="inner">
        content
    </div>
</div>

First option: use pseudo-elements, here vertical and horizontal spacing are relative to the outer. Demo

#outer::before, #outer::after {
    display: block;
    content: "";
    height: 10%;
}
#inner {
    height: 80%;
    margin-left: 10%;
    margin-right: 10%;
}

Moving the horizontal spacing to the outer element makes it relative to the parent of the outer. Demo

#outer {
    padding-left: 10%;
    padding-right: 10%;
}

Second option: use absolute positioning. Demo

#outer {
    position: relative;
}
#inner {
    position: absolute;
    left: 10%;
    right: 10%;
    top: 10%;
    bottom: 10%;
}

a
alain

To make the child element positioned absolutely from its parent element you need to set relative position on the parent element AND absolute position on the child element.

Then on the child element 'top' is relative to the height of the parent. So you also need to 'translate' upward the child 50% of its own height.

.base{ background-color: green; width: 200px; height: 200px; overflow: auto; position: relative; } .vert-align { position: absolute; top: 50%; transform: translate(0, -50%); }

Content Here

There is another a solution using flex box.

.base{ background-color:green; width: 200px; height: 200px; overflow: auto; display: flex; align-items: center; }

Content Here

You will find advantages/disavantages for both.


Using translate is the only real way of moving an element vertically relative to its own size.
Thanks for this. This should be the chosen answer.
J
James T

This can be achieved with the writing-mode property. If you set an element's writing-mode to a vertical writing mode, such as vertical-lr, its descendants' percentage values for padding and margin, in both dimensions, become relative to height instead of width.

From the spec:

. . . percentages on the margin and padding properties, which are always calculated with respect to the containing block width in CSS2.1, are calculated with respect to the inline size of the containing block in CSS3.

The definition of inline size:

A measurement in the inline dimension: refers to the physical width (horizontal dimension) in horizontal writing modes, and to the physical height (vertical dimension) in vertical writing modes.

Example, with a resizable element, where horizontal margins are relative to width and vertical margins are relative to height.

.resize { width: 400px; height: 200px; resize: both; overflow: hidden; } .outer { height: 100%; background-color: red; } .middle { writing-mode: vertical-lr; margin: 0 10%; width: 80%; height: 100%; background-color: yellow; } .inner { writing-mode: horizontal-tb; margin: 10% 0; width: 100%; height: 80%; background-color: blue; }

Using a vertical writing mode can be particularly useful in circumstances where you want the aspect ratio of an element to remain constant, but want its size to scale in correlation to its height instead of width.


This is, IMO, would be the more elegant solution, but (as of 2018-05-05), vertical writing modes aren't well supported. Hopefully that changes soon.
@zanerock I'm fairly certain that MDN compat table is outdated. For instance, it says that Safari needs the -webkit- prefix, but according to caniuse it hasn't required the prefix since Safari 11. Did you try it with any browser in which it didn't work?
No, I have to admit I just assumed it was good, automated even.
It might be best to use writing-mode: revert; on the inside: codepen.io/sollyu/pen/dyJQyze?editors=1100
r
rawiro

Other way to center one line text is:

.parent{
  position: relative;
}

.child{
   position: absolute;
   top: 50%;
   line-height: 0;
}

or just

.parent{
  overflow: hidden; /* if this ins't here the parent will adopt the 50% margin of the child */
}

.child{
   margin-top: 50%;
   line-height: 0;
}

S
SpliFF

A 50% padding wont center your child, it will place it below the center. I think you really want a padding-top of 25%. Maybe you're just running out of space as your content gets taller? Also have you tried setting the margin-top instead of padding-top?

EDIT: Nevermind, the w3schools site says

% Specifies the padding in percent of the width of the containing element

So maybe it always uses width? I'd never noticed.

What you are doing can be acheived using display:table though (at least for modern browsers). The technique is explained here.


Thanks Spliff. I understand that my 50% will not center the content of the div. I actually have only one line of text that needs to be centered vertically. Thanks a lot for the link though!
If it's just one line of text have you considered using a ridiculously large line-height, ie, make the line-height 200px?
@Ryan for centering text vertically, see stackoverflow.com/questions/8865458/…
your link is broken
W
WoodrowShigeru

This is a very interesting bug. (In my opinion, it is a bug anyway) Nice find!

Regarding how to set it, I would recommend Camilo Martin's answer. But as to why, I'd like to explain this a bit if you guys don't mind.

In the CSS specs I found:

'padding' Percentages: refer to width of containing block

… which is weird, but okay.

So, with a parent width: 210px and a child padding-top: 50%, I get a calculated/computed value of padding-top: 96.5px – which is not the expected 105px.

That is because in Windows (I'm not sure about other OSs), the size of common scrollbars is per default 17px × 100% (or 100% × 17px for horizontal bars). Those 17px are substracted before calculating the 50%, hence 50% of 193px = 96.5px.


There is a reason for the spec, and this explains it better: hongkiat.com/blog/calculate-css-percentage-margins
If you use "writing-mode: vertical-lr;" it appears to make the percentages refer to the height of the containing block, at least in Chrome 88.