Translate X and Y percentage values based on elements height and width?
Asked Answered
H

10

106

Translating an elements Y axis 50% will move it down 50% of its own height, not 50% of the parents height as I would expect. How do I tell a translating element to base it's translation percentage on the parent element? Or am I not understanding something?

http://jsfiddle.net/4wqEm/2/

Horseshoes answered 22/6, 2012 at 16:21 Comment(3)
Alright, it looks like there's no way around it. CSS translation by percentage takes the % of the element that is being translated to figure the distance to move. It does not act like your typical css declaration like top, margin-top, or padding-top, which are all based on parent container.Horseshoes
Thanks, it wasn't immediately apparent to me what the percentage value was a percentage of.Waterresistant
Next thing you know, they'll make percentages mean percentage of the font-size too! (oh...)Soap
S
12

In browsers where it's supported, you can now use container units to translate an element by a percentage of the parent width/height.

First set the container-type property of the parent to "size". This enables children to query the parent dimensions.

.parent { container-type: size }

Next translate the child element with cqh (container query height) units. 1cqh equals 1% of the parent height.

/* shift child down by 50% of parent height */
.child { transform: translateY(50cqh) }
Sandra answered 27/5, 2023 at 16:53 Comment(1)
Well, the day has finally come. Thanks for supplying a direct, working solution to the exact problem, Chris!Horseshoes
A
54

When using percentage in a transform translate on a non-SVG element, it refers to the width or height of itself. Take a look at https://davidwalsh.name/css-vertical-center (demo):

One interesting thing about CSS transforms is that, when applying them with percentage values, they base that value on the dimensions of the element which they are being implemented on, as opposed to properties like top, right, bottom, left, margin, and padding, which only use the parent's dimensions (or in case of absolute positioning, which uses its closest relative parent).

On an SVG element, a transform percentage refers to the size of the parent instead!

Here is a pen:

https://codepen.io/trusktr/pen/gOdwWXv

svg, [outer] {
  border: 1px solid black;
}

rect {
  transform: translate3d(50%, 50%, 0);
}

[inner] {
  background: black;
  transform: translate3d(50%, 50%, 0);
}
<svg width="100" height="80">
    <rect width="20" height="20" />
</svg>

<div outer style="width: 100px; height: 80px;">
    <div inner style="width: 20px; height: 20px;"></div>
</div>

Strange, huh?

Agathaagathe answered 17/1, 2014 at 0:28 Comment(2)
But in animating an SVG, transform: translate(50%) seems to use the parent element's width and heightLodged
@Lodged That is an odd thing indeed. They should just give us both options and stop the oddities.Nordstrom
C
26

You can use vw and vh to translate based on the viewport size

@keyframes bubbleup {
  0% {
    transform: translateY(100vh);
  }

  100% {
    transform: translateY(0vh);
  }
}
Crispa answered 13/4, 2016 at 13:46 Comment(5)
This doesn't work for me... vh ~40 is like the bottom of the page?!Jesicajeske
It was my bad... the element wasn't inside an absolute element... :)Jesicajeske
@AxeEffect This should not be the accepted answer. This is relative to viewport, not parent element. What if the parent element is not full size of the viewport?Nordstrom
@wolfdawn if you use scaling, order matters. So "scale(1.5) translateY(40vw)" i not the same as "translateY(40vw) scale(1.5)".Vestment
You should know that Apple and Google have broken the vh unit in Mobile browsers and so it's unreliable in that case.Deedee
E
25

What works for me using only CSS is:

.child {
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    
    /* Backward compatibility */
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
}

How it works:

  • top and left positioning move child widget according to parent coordinates. Child widget's top-left corner will appear exactly in the center of the parent (this is not what we want at this point).
  • translate moves child widget -50% up and left based on its size (not the parent). It means, widget's center point moves exactly where top-left point was (previously set up as center of a parent) - and this is what we want.
Endeavor answered 9/12, 2016 at 12:17 Comment(0)
S
12

In browsers where it's supported, you can now use container units to translate an element by a percentage of the parent width/height.

First set the container-type property of the parent to "size". This enables children to query the parent dimensions.

.parent { container-type: size }

Next translate the child element with cqh (container query height) units. 1cqh equals 1% of the parent height.

/* shift child down by 50% of parent height */
.child { transform: translateY(50cqh) }
Sandra answered 27/5, 2023 at 16:53 Comment(1)
Well, the day has finally come. Thanks for supplying a direct, working solution to the exact problem, Chris!Horseshoes
M
7

To use percentage in the translate property, you have to use Javascript : http://jsfiddle.net/4wqEm/27/

HTML code :

<div id="parent">
    <div id="children"></div>
</div>​​​​​​​​​​​​​

CSS code :

#parent {
    width: 200px;
    height: 200px;
    background: #ff0;
}
#children {
    width: 10%;
    height: 10%;
    background: #f00;
}

Javascript code :

parent = document.getElementById('parent');
children = document.getElementById('children');

parent_height = parent.clientHeight;
​children_translate = parent_height * 50/100;
children.style.webkitTransform = "translateY("+children_translate+"px)";​​​​​​​​​​​​​​​​​​​​​​​​​​

I hope I could help you and say me if you have any other problem.

Marge answered 30/12, 2012 at 13:50 Comment(1)
i can't do it only with CSS3?Punk
A
4

You can also use one extra block and use the transition for it except the child node

HTML code :

<div id="parent">
    <div id="childrenWrapper">    
        <div id="children"></div>
    </div>
</div>​​​​​​​​​​​​​

css should be something like this

#parent {
    position: relative;
    width: 200px;
    height: 200px;
    background: #ff0;
}
#childrenWrapper{
    width: 100%;
    height: 100%;
}
#children {
    width: 10%;
    height: 10%;
    background: #f00;
}
Adjunct answered 2/10, 2014 at 12:10 Comment(0)
I
4

Your statement is absolutely right about the percentages coming from the very translated element. Instead of using translate property in your case you should be using absolute positioning to stay relative to the parent div. I absolutely positioned vertically your red div here:(don`t forget about adding position relative to the parent div.It has to be positioned other than static default):

js fiddle pen here

 body {
    margin: 0;
    width: 100%;
    height: 100%;
    }
 body > div {
    width: 200px;
    height: 200px;
    background: #ff0;
    position: relative;
    }
 body > div > div {
    width: 10%;
    height: 10%;
    -webkit-transform: translateY(-50%);
    background: #f00;
    position: absolute;
    top: 50%;
    }
Improbability answered 5/1, 2016 at 10:44 Comment(2)
But animating top and left is not hardware accelerated, is it?Nordstrom
@Nordstrom yes and it ends up with sluggish animations depending on the number of nodes.Fort
T
1

Its forked with positioning required on the following URL working sample

body {
margin: 0;
width: 100%;
height: 100%;
}

body>div {
position: relative;
width: 200px;
height: 200px;
background: #ff0;
}

body>div>div {
position: absolute;
left: 50%;
top: 50%;
width: 10%;
height: 10%;
background: #f00;
transform: translate(-50%, -50%);
}

notes :

  1. you can absolute positioning of your red square by changing parent element to position relative
  2. then using 50% top and 50% left will position red square according to its upper left corner
  3. using transform:translate(-50%,-50%) will position red square according to its center
Tasman answered 26/5, 2018 at 13:55 Comment(0)
A
0

You can make the element absolute positioned and use left and top property to take the percentage value as parent.

Appeal answered 17/6, 2015 at 5:6 Comment(1)
Not recommended. Ends up with sluggish animations.Fort
C
0

The solution to this problem is not to use translate at all. When you are translating an element, the percentage you select is based on it's own height.

If you want to position the element based on the parent's height, use top: 50%;

So the code will look like this:

body {
    margin: 0;
    width: 100%;
    height: 100%;
}
body > div {
    width: 200px;
    height: 200px;
    background: #ff0;
    position: relative;
}
body > div > div {
    position: absolute;
    top: 50%;
    width: 10%;
    height: 10%;
/*     -webkit-transform: translateY(50%); */
    background: #f00;
}
Cunningham answered 18/5, 2018 at 13:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.