CSS 中的旋转元素正确影响其父元素的高度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16301625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rotated elements in CSS that affect their parent's height correctly
提问by Chris
Let's say I have a couple of columns, of which some I'd like to rotate the values of:
假设我有几列,其中一些我想旋转以下值:
<div class="container">
<div class="statusColumn"><span>Normal</span></div>
<div class="statusColumn"><a>Normal</a></div>
<div class="statusColumn"><b>Rotated</b></div>
<div class="statusColumn"><abbr>Normal</abbr></div>
</div>
With this CSS:
使用这个 CSS:
.statusColumn b {
writing-mode: tb-rl;
white-space: nowrap;
display: inline-block;
overflow: visible;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
It ends up looking like this:
它最终看起来像这样:
Is it possible to write any CSS that will cause the rotated element to affect its parent's height, such that the text would not overlap the other elements? Something like this:
是否可以编写任何会导致旋转元素影响其父元素高度的 CSS,从而使文本不会与其他元素重叠?像这样的东西:
采纳答案by Mark Amery
Assuming that you want to rotate 90 degrees, this is possible, even for non-text elements - but like many interesting things in CSS, it requires a little cunning. My solution also technically invokes undefined behaviour according to the CSS 2 spec - so while I've tested and confirmed that it works in Chrome, Firefox, Safari, and Edge, I can't promise you that it won't break in a future browser release.
假设你想旋转 90 度,这是可能的,即使对于非文本元素 - 但就像 CSS 中的许多有趣的东西一样,它需要一点狡猾。我的解决方案还根据 CSS 2 规范在技术上调用了未定义的行为 - 所以虽然我已经测试并确认它在 Chrome、Firefox、Safari 和 Edge 中有效,但我不能向你保证它将来不会中断浏览器发布。
Short answer
简答
Given HTML like this, where you want to rotate .element-to-rotate
...
给定这样的 HTML,您想要旋转的位置.element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduce twowrapper elements around the element that you want to rotate:
...在要旋转的元素周围引入两个包装元素:
<div id="container">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<something class="element-to-rotate">bla bla bla</something>
</div>
</div>
</div>
... and then use the following CSS, to rotate anti-clockwise (or see the commented out transform
for a way to change it into a clockwise rotation):
...然后使用以下 CSS 逆时针旋转(或查看注释以transform
了解将其更改为顺时针旋转的方法):
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
Stack snippet demo
堆栈片段演示
p {
/* Tweak the visuals of the paragraphs for easier visualiation: */
background: pink;
margin: 1px 0;
border: 1px solid black;
}
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
<div id="container">
<p>Some text</p>
<p>More text</p>
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<p class="element-to-rotate">Some rotated text</p>
</div>
</div>
<p>Even more text</p>
<img src="https://i.stack.imgur.com/ih8Fj.png">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<img class="element-to-rotate" src="https://i.stack.imgur.com/ih8Fj.png">
</div>
</div>
<img src="https://i.stack.imgur.com/ih8Fj.png">
</div>
How does this work?
这是如何运作的?
Confusion in the face of the incantations I've used above is reasonable; there's a lot going on, and the overall strategy is not straightforward and requires some knowledge of CSS trivia to understand. Let's go through it step by step.
面对我上面使用的咒语,困惑是合理的;有很多事情要做,整体策略并不简单,需要一些 CSS 琐事知识才能理解。让我们一步一步来。
The core of the problem we face is that transformations applied to an element using its transform
CSS property all happen after layout has taken place. In other words, using transform
on an element does not affect the size or position of its parent or of any other elements, at all. There is absolutely no way to change this fact of how transform
works. Thus, in order to create the effect of rotating an element and having its parent's height respect the rotation, we need to do the following things:
我们面临的问题的核心是使用其transform
CSS 属性应用于元素的转换都发生在布局发生之后。换句话说,transform
在元素上使用根本不会影响其父元素或任何其他元素的大小或位置。绝对没有办法改变这个transform
工作原理的事实。因此,为了创建旋转元素并使其父元素的高度尊重旋转的效果,我们需要做以下事情:
- Somehow construct some otherelement whose height is equal to the width of the
.element-to-rotate
- Write our transform on
.element-to-rotate
such as to overlay it exactly on the element from step 1.
- 以某种方式构造一些其他元素,其高度等于
.element-to-rotate
- 将我们的变换写在
.element-to-rotate
例如将它精确地覆盖在步骤 1 中的元素上。
The element from step 1 shall be .rotation-wrapper-outer
. But how can we cause its heightto be equal to .element-to-rotate
's width?
步骤 1 中的元素应为.rotation-wrapper-outer
。但是我们怎样才能使它的高度等于.element-to-rotate
的宽度呢?
The key component in our strategy here is the padding: 50% 0
on .rotation-wrapper-inner
. This exploit's an eccentric detail of the spec for padding
: that percentage paddings, even for padding-top
and padding-bottom
, are always defined as percentages of the width of the element's container. This enables us to perform the following two-step trick:
我们策略中的关键组成部分是padding: 50% 0
on .rotation-wrapper-inner
。这个漏洞是规范padding
的一个古怪细节:百分比填充,即使是padding-top
和padding-bottom
,总是被定义为元素容器宽度的百分比。这使我们能够执行以下两步技巧:
- We set
display: table
on.rotation-wrapper-outer
. This causes it to have shrink-to-fit width, which means that its width will be set based upon the intrinsic width of its contents - that is, based upon the intrinsic width of.element-to-rotate
. (On supporting browsers, we could achieve this more cleanly withwidth: max-content
, but as of December 2017,max-content
is still not supported in Edge.) - We set the
height
of.rotation-wrapper-inner
to 0, then set its padding to50% 0
(that is, 50% top and 50% bottom). This causes it to take up vertical space equal to 100% of the width of its parent - which, through the trick in step 1, is equal to the width of.element-to-rotate
.
- 我们开始
display: table
了.rotation-wrapper-outer
。这导致它具有缩小以适应宽度,这意味着它的宽度将根据其内容的固有宽度设置 - 即基于 的固有宽度.element-to-rotate
。(在支持的浏览器,我们可以更干净地做到这一点width: max-content
,但截至2017年12月,max-content
是仍然不支持边)。 - 我们将
height
of设置.rotation-wrapper-inner
为 0,然后将其填充设置为50% 0
(即 50% 顶部和 50% 底部)。这导致它占用的垂直空间等于其父级宽度的 100% - 通过步骤 1 中的技巧,它等于.element-to-rotate
.
Next, all that remains is to perform the actual rotation and positioning of the child element. Naturally, transform: rotate(-90deg)
does the rotation; we use transform-origin: top left;
to cause the rotation to happen around the top-left corner of the rotated element, which makes the subsequent translation easier to reason about, since it leaves the rotated element directly above where it would otherwise have been drawn. We can then use translate(-100%)
to drag the element downwards by a distance equal to its pre-rotation width.
接下来,剩下的就是执行子元素的实际旋转和定位。自然地,transform: rotate(-90deg)
旋转;我们使用transform-origin: top left;
来使旋转发生在旋转元素的左上角周围,这使得后续的平移更容易推理,因为它将旋转元素直接留在原本绘制的位置上方。然后我们可以使用translate(-100%)
将元素向下拖动等于其预旋转宽度的距离。
That still doesn't quite get the positioning right, because we still need to offset for the 50% top padding on .rotation-wrapper-outer
. We achieve that by ensuring that .element-to-rotate
has display: block
(so that margins will work properly on it) and then applying a -50% margin-top
- note that percentage margins are alsodefined relative to the width of the parent element.
这仍然没有完全正确定位,因为我们仍然需要偏移 .50% 的顶部填充.rotation-wrapper-outer
。我们通过确保.element-to-rotate
具有display: block
(以便边距可以在其上正常工作)然后应用 -50% 来实现这一点margin-top
- 请注意,百分比边距也是相对于父元素的宽度定义的。
And that's it!
就是这样!
Why isn't this fully spec-compliant?
为什么这不完全符合规范?
Because of the following note from the definition of percentage paddings and margins in the spec(bolding mine):
由于规范中百分比填充和边距的定义中的以下注释(我的粗体):
The percentage is calculated with respect to the widthof the generated box's containing block, even for 'padding-top'and 'padding-bottom'. If the containing block's width depends on this element, then the resulting layout is undefined in CSS 2.1.
百分比是根据生成的框的包含块的宽度计算的,即使对于'padding-top'和'padding-bottom' 也是如此。如果包含块的宽度取决于此元素,则生成的布局在 CSS 2.1 中未定义。
Since the entire trick revolved around making the padding of the inner wrapper element be relative to the width of its container which was in turn dependent upon the width of its child, we're hitting this condition and invoking undefined behaviour. It currently works in all 4 major browsers, though - unlike some seemingly spec-compliant tweaks to approach that I've tried, like changing .rotation-wrapper-inner
to be a sibling of .element-to-rotate
instead of a parent.
由于整个技巧围绕使内部包装元素的填充与其容器的宽度相关,而容器的宽度又取决于其子元素的宽度,因此我们遇到了这种情况并调用了未定义的行为。不过,它目前适用于所有 4 种主要浏览器 - 与我尝试过的一些看似符合规范的调整方法不同,例如更改.rotation-wrapper-inner
为兄弟.element-to-rotate
而不是父浏览器。
回答by MMM
Unfortunately (?) this is how it's suppose to work even though you rotate your element it still has certain width and height, that does not change after rotation. You visually change it, but there is no invisible wrapping box that changes its size when you rotate things.
不幸的是(?)这就是它的工作方式,即使您旋转元素它仍然具有一定的宽度和高度,旋转后不会改变。您可以在视觉上对其进行更改,但是没有不可见的包装盒会在您旋转事物时更改其大小。
Imagine rotating it less than 90° (e.g. transform: rotate(45deg)
): you would have to introduce such invisible box which now has ambiguous dimensions based on the original dimensions of the object you're rotating and the actual rotation value.
想象一下将它旋转小于 90°(例如transform: rotate(45deg)
):您将不得不引入这样的不可见框,它现在具有基于您正在旋转的对象的原始尺寸和实际旋转值的模糊尺寸。
Suddenly, you do not only have the width
and height
of the object you have rotated, but you also have the width
and height
of the "invisible box" around it. Imagine requesting the outer width of this object - what would it return? The width of the object, or our new box? How would we distinguish between both?
突然之间,您不仅拥有旋转对象的width
和height
,而且还有围绕它的“隐形盒子”的width
和height
。想象一下请求这个对象的外部宽度 - 它会返回什么?物体的宽度,还是我们的新盒子?我们如何区分两者?
Therefore, there is no CSS that you can write to fix this behavior (or should I say, "automate" it). Of course you can increase the size of your parent container by hand, or write some JavaScript to handle that.
因此,没有您可以编写的 CSS 来修复这种行为(或者我应该说,“自动化”它)。当然,您可以手动增加父容器的大小,或者编写一些 JavaScript 来处理它。
(Just to be clear, you can try using element.getBoundingClientRect
to get the rectangle mentioned before).
(为了清楚起见,您可以尝试使用element.getBoundingClientRect
来获取前面提到的矩形)。
如规范中所述:
In the HTML namespace, the transform property does not affect the flow of the content surrounding the transformed element.
在 HTML 命名空间中,transform 属性不会影响被转换元素周围的内容流。
This means that no changes will be made to the content surrounding the object you're transforming, unless you do them by hand.
这意味着不会对您正在转换的对象周围的内容进行任何更改,除非您手动进行更改。
The only thing that is taken into account when you transform your object is the overflow area:
变换对象时唯一要考虑的是溢出区域:
(...) the extent of the overflow area takes into account transformed elements. This behavior is similar towhat happens when elements are offset via relative positioning.
(...) 溢出区域的范围考虑了转换后的元素。这种行为类似于通过相对定位偏移元素时发生的情况。
See this jsfiddle to learn more.
It's actually quite good to compare this situation to an object offset using: position: relative
- the surrounding content does not change, even though you're moving your object around (example).
将这种情况与使用以下方法的对象偏移进行比较实际上非常好:position: relative
- 即使您正在四处移动对象(示例),周围的内容也不会改变。
If you want to handle this using JavaScript, have a look at thisquestion.
回答by Bram Vanroy
As the comment by G-Cyrrightfully points out, current support for writing-mode
is more than decent. Combined with a simple rotate, you get the exact result that you want. See the example below.
作为由G圣西尔评论理所当然地指出,当前支持writing-mode
比体面的多。结合简单的旋转,您可以获得所需的确切结果。请参阅下面的示例。
.statusColumn {
position: relative;
border: 1px solid #ccc;
padding: 2px;
margin: 2px;
width: 200px;
}
.statusColumn i,
.statusColumn b,
.statusColumn em,
.statusColumn strong {
writing-mode: vertical-rl;
transform: rotate(180deg);
white-space: nowrap;
display: inline-block;
overflow: visible;
}
<div class="container">
<div class="statusColumn"><span>Normal</span></div>
<div class="statusColumn"><a>Normal</a></div>
<div class="statusColumn"><b>Rotated</b></div>
<div class="statusColumn"><abbr>Normal</abbr></div>
</div>
回答by Litek
Use percentages for padding
and a pseudo element to push the content. In the JSFiddle I left the pseudo element red to show it and you'll have to compensate the shift of the text but I think that's the way to go.
使用百分比padding
和伪元素来推送内容。在 JSFiddle 中,我将伪元素保留为红色以显示它,您必须补偿文本的偏移,但我认为这是要走的路。
.statusColumn {
position: relative;
border: 1px solid #ccc;
padding: 2px;
margin: 2px;
width: 200px;
}
.statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong {
writing-mode: tb-rl;
white-space: nowrap;
display: inline-block;
overflow: visible;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
/* also accepts left, right, top, bottom coordinates; not required, but a good idea for styling */
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
-o-transform-origin: 50% 50%;
transform-origin: 50% 50%;
/* Should be unset in IE9+ I think. */
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
}
.statusColumn b:before{ content:''; padding:50% 0; display:block; background:red; position:relative; top:20px
}
<div class="container">
<div class="statusColumn"><span>Normal</span></div>
<div class="statusColumn"><a>Normal</a></div>
<div class="statusColumn"><b>Rotated</b></div>
<div class="statusColumn"><abbr>Normal</abbr></div>
</div>
A write-up of this solution can be found here: http://kizu.ru/en/fun/rotated-text/
可以在此处找到此解决方案的文章:http: //kizu.ru/en/fun/rotated-text/
回答by Bram Vanroy
Please see the updated version in my other answer. This answer is no longer valid and simply does not work any more. I'll leave it here for historical reasons.
请参阅我的其他答案中的更新版本。这个答案不再有效,并且不再有效。由于历史原因,我会把它留在这里。
This question is quite old, but with the current support for the .getBoundingClientRect()
object and its width
and height
values, in combination with the ability to use this neat method together with transform
, I think my solution should be mentioned as well.
这个问题是很老,但对于目前的支持.getBoundingClientRect()
对象和它的width
和height
值,在与一起使用此方法整齐的能力相结合transform
,我觉得我的解决方案应该提到的为好。
See it in action here. (Tested in Chrome 42, FF 33 & 37.)
在这里查看它的实际效果。(在 Chrome 42、FF 33 和 37 中测试。)
getBoundingClientRect
calculates the actual boxwidth and height of the element. Quite simply, then, we can loop through all elements and set its min-height to the actual box height of their children.
getBoundingClientRect
计算元素的实际框宽度和高度。很简单,然后,我们可以遍历所有元素并将其 min-height 设置为其子元素的实际框高度。
$(".statusColumn").each(function() {
var $this = $(this),
child = $this.children(":first");
$this.css("minHeight", function() {
return child[0].getBoundingClientRect().height;
});
});
(With some modification you can loop through the children and find out which one's the tallest and set the height of the parent to the tallest child. However, I decided to make the example more straightforward. You can also add padding of the parent to the height if you want, or you can use a relevant box-sizing
value in CSS.)
(通过一些修改,您可以遍历孩子并找出哪个最高,并将父级的高度设置为最高的子级。但是,我决定使示例更简单。您还可以将父级的填充添加到如果需要,也可以使用高度,或者您可以box-sizing
在 CSS 中使用相关值。)
Note, though, that I added a translate
and transform-origin
to your CSS to make the positioning more flexible and accurate.
但是请注意,我在您的 CSS 中添加了一个translate
和transform-origin
以使定位更加灵活和准确。
transform: rotate(-90deg) translateX(-100%);
transform-origin: top left;
回答by vic_sk
I know it's an old post but I found it while struggling with exactly the same problem. The solution that works for me is rather crude "low-tech" method by simply surround the div I rotate by 90deg with a lot of
我知道这是一个旧帖子,但我在解决完全相同的问题时发现了它。对我有用的解决方案是相当粗糙的“低技术”方法,只需将我旋转 90 度的 div 用很多
<br>
Knowing the approximate width (which becomes height after rotation) of div I can compensate the difference by adding br's around this div so content above and below gets pushed accordingly.
知道 div 的大致宽度(旋转后变为高度)我可以通过在此 div 周围添加 br 来补偿差异,以便相应地推动上下内容。