Disons que j'ai quelques colonnes, certaines desquelles j'aimerais faire pivoter les valeurs de:
<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>
Avec ce CSS:
.statusColumn b {
writing-mode: tb-rl;
white-space: nowrap;
display: inline-block;
overflow: visible;
transform: rotate(-90deg);
transform-Origin: 50% 50%;
}
Cela finit par ressembler à ceci:
Est-il possible d'écrire n'importe quel CSS qui fera que l'élément pivoté affecte la hauteur de son parent, de telle sorte que le texte ne chevauche pas les autres éléments? Quelque chose comme ça:
En supposant que vous souhaitiez effectuer une rotation de 90 degrés, cela est possible, même pour des éléments autres que du texte. Toutefois, comme beaucoup de choses intéressantes en CSS, cela nécessite un peu de ruse. Ma solution invoque également techniquement un comportement indéfini conformément à la spécification CSS 2. Ainsi, même si j'ai testé et confirmé son fonctionnement sous Chrome, Firefox, Safari et Edge, je ne peux pas vous promettre que cela ne se cassera pas dans le futur. version du navigateur.
Étant donné le code HTML comme celui-ci, où vous souhaitez faire pivoter .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduisez deux envelopper les éléments autour de l'élément que vous voulez faire pivoter:
<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>
... et utilisez ensuite le code CSS suivant pour effectuer une rotation anti-horaire (ou consultez le commentaire transform
pour obtenir un moyen de le modifier en rotation horaire):
.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;
}
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>
La confusion devant les incantations que j'ai utilisées ci-dessus est raisonnable; il y a beaucoup de choses en cours, et la stratégie globale n'est pas simple et nécessite une certaine connaissance de trivia CSS pour comprendre. Passons en revue étape par étape.
Le problème principal auquel nous sommes confrontés est que les transformations appliquées à un élément à l'aide de sa propriété CSS transform
se produisent toutes après la mise en page. En d'autres termes, l'utilisation de transform
sur un élément n'a aucune incidence sur la taille ou la position de son parent ou de tout autre élément. Il n'y a absolument aucun moyen de changer ce fait de la façon dont transform
fonctionne. Ainsi, afin de créer l'effet de rotation d'un élément et de faire en sorte que la hauteur de son parent respecte la rotation, nous devons procéder comme suit:
.element-to-rotate
.element-to-rotate
De manière à la superposer exactement à l'élément de l'étape 1.L'élément de l'étape 1 doit être .rotation-wrapper-outer
. Mais comment pouvons-nous faire en sorte que ses hauteur soient égaux à .element-to-rotate
largeur?
Le composant clé de notre stratégie ici est le padding: 50% 0
Sur .rotation-wrapper-inner
. Cet exploit n détail excentrique de la spécification pour padding
: ce pourcentage de remplissage, même pour padding-top
Et padding-bottom
, Est toujours défini en tant que pourcentage du largeur du conteneur de l'élément. Cela nous permet d'effectuer le tour suivant en deux étapes:
display: table
Sur .rotation-wrapper-outer
. Cela le fait avoir largeur de retrait , ce qui signifie que sa largeur sera définie en fonction de la largeur intrinsèque de son contenu - c'est-à-dire en fonction de la largeur intrinsèque de .element-to-rotate
. (Sur les navigateurs compatibles, nous pourrions réaliser cela plus proprement avec width: max-content
, Mais à partir de décembre 2017, max-content
Est toujours pas pris en charge dans Edge .)height
de .rotation-wrapper-inner
Sur 0, puis nous réglons son remplissage sur 50% 0
(C'est-à-dire 50% en haut et 50% en bas). Cela lui fait occuper un espace vertical égal à 100% de la largeur de son parent - ce qui, par le truc de l'étape 1, est égal à la largeur de .element-to-rotate
.Il ne reste ensuite qu’à effectuer la rotation et le positionnement réels de l’élément enfant. Naturellement, transform: rotate(-90deg)
effectue la rotation; nous utilisons transform-Origin: top left;
pour que la rotation se produise autour du coin supérieur gauche de l'élément pivoté, ce qui permet de raisonner facilement la traduction suivante, car il laisse l'élément pivoté directement au-dessus de l'endroit où il aurait autrement été dessiné . Nous pouvons ensuite utiliser translate(-100%)
pour faire glisser l'élément vers le bas d'une distance égale à sa largeur de pré-rotation.
Le positionnement n’est pas encore tout à fait correct, car nous devons toujours compenser le remplissage supérieur à 50% sur .rotation-wrapper-outer
. Nous y parvenons en veillant à ce que .element-to-rotate
Dispose de display: block
(Pour que les marges fonctionnent correctement), puis en appliquant un -50% margin-top
- notez que les marges de pourcentage sont also défini par rapport à la largeur de l'élément parent.
Et c'est tout!
En raison de la note suivante de la définition des pourcentages de remplissage et des marges dans la spécification (mine en gras):
Le pourcentage est calculé par rapport à largeur de la boîte générée bloc contenant , même pour 'padding-top' et 'padding-bottom' . Si la largeur du bloc contenant dépend de cet élément, la présentation résultante n'est pas définie en CSS 2.1.
Puisque tout le truc était de rendre le rembourrage de l'élément d'enveloppe interne relatif par rapport à la largeur de son conteneur, qui dépendait à son tour de la largeur de son enfant, nous atteignons cette condition et invoquons un comportement non défini. Cependant, il fonctionne actuellement dans les 4 principaux navigateurs - contrairement à certains ajustements apparemment conformes aux spécifications que j'ai essayé, comme changer de .rotation-wrapper-inner
Pour devenir un frère de .element-to-rotate
Au lieu d'un parent.
Malheureusement (?) C'est ainsi qu'il est supposé fonctionner même si vous faites pivoter votre élément, il a toujours une certaine largeur et hauteur, cela ne change pas après la rotation. Vous le modifiez visuellement, mais il n'y a pas de boîte d'emballage invisible qui change de taille lorsque vous faites pivoter les éléments.
Imaginez que vous le faites pivoter à moins de 90 ° (par exemple, transform: rotate(45deg)
): vous devez introduire un tel boîtier invisible, qui a maintenant des dimensions ambiguës basées sur les dimensions d'origine de l'objet que vous faites pivoter et la valeur de rotation réelle.
Du coup, vous n’avez pas seulement les width
et height
de l’objet tourné, mais également les width
et height
du "invisible". boîte "autour de lui. Imaginez-vous demander la largeur extérieure de cet objet - que retournera-t-il? La largeur de l'objet ou notre nouvelle boîte? Comment distinguerions-nous les deux?
Par conséquent, il n'y a pas de CSS que vous pouvez écrire pour résoudre ce problème (ou devrais-je dire, "automatisez-le"). Bien sûr, vous pouvez augmenter la taille de votre conteneur parent à la main ou écrire du code JavaScript pour gérer cela.
(Juste pour être clair, vous pouvez essayer d'utiliser element.getBoundingClientRect
pour obtenir le rectangle mentionné précédemment).
Comme décrit dans la spécification :
Dans l'espace de noms HTML, la propriété de transformation n'affecte pas le flux du contenu entourant l'élément transformé.
Cela signifie qu'aucune modification ne sera apportée au contenu entourant l'objet en cours de transformation, à moins que vous ne le fassiez à la main.
La seule chose qui est prise en compte lorsque vous transformez votre objet est la zone de débordement:
(...) l'étendue de la zone de débordement prend en compte les éléments transformés. Ce comportement est similaire à ce qui se produit lorsque les éléments sont décalés via positionnement relatif .
Voir ce jsfiddle pour en savoir plus.
En fait, il est bon de comparer cette situation à un décalage d'objet avec: position: relative
- le contenu environnant ne change pas, même si vous déplacez votre objet ( exemple ).
Si vous voulez gérer cela en utilisant JavaScript, jetez un oeil à this question.
Utilisez les pourcentages pour padding
et un pseudo-élément pour transmettre le contenu. Dans le JSFiddle, j'ai laissé le pseudo-élément en rouge pour le montrer et vous devrez compenser le décalage du texte, mais je pense que c'est la voie à suivre.
.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>
Une description de cette solution peut être trouvée ici: http://kizu.ru/en/fun/rotated-text/
Comme le commentaire de G-Cyr souligne à juste titre, support actuel pour writing-mode
est plus que décent . Combiné à une simple rotation, vous obtenez le résultat exact que vous souhaitez. Voir l'exemple ci-dessous.
.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>
Veuillez voir la version mise à jour dans mon autre réponse . Cette réponse n'est plus valide et ne fonctionne tout simplement plus. Je vais le laisser ici pour des raisons historiques.
Cette question est assez ancienne, mais avec le support actuel pour l'objet .getBoundingClientRect()
et ses valeurs width
et height
, en combinaison avec la capacité pour utiliser cette méthode soignée avec transform
, je pense que ma solution devrait également être mentionnée.
Voyez-le en action ici . (Testé dans Chrome 42, FF 33 et 37.)
getBoundingClientRect
calcule la largeur réelle de l'élément. Tout simplement, alors, nous pouvons parcourir tous les éléments et définir sa hauteur minimale sur la hauteur réelle de la boîte de leurs enfants.
$(".statusColumn").each(function() {
var $this = $(this),
child = $this.children(":first");
$this.css("minHeight", function() {
return child[0].getBoundingClientRect().height;
});
});
(Avec quelques modifications, vous pouvez parcourir les enfants pour trouver lequel est le plus grand et définir la taille du parent sur le plus grand. Cependant, j’ai décidé de simplifier l’exemple. Vous pouvez également ajouter un rembourrage du parent au hauteur si vous voulez, ou vous pouvez utiliser un box-sizing
valeur en CSS.)
Notez, cependant, que j’ai ajouté un translate
et un transform-Origin
à votre CSS pour rendre le positionnement plus flexible et précis.
transform: rotate(-90deg) translateX(-100%);
transform-Origin: top left;
La propriété en mode écriture le fera. Support actuel pour writing-mode
est plus que décent . Combiné à une simple rotation, vous obtenez le résultat exact que vous souhaitez. Voir l'exemple ci-dessous.
.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>
(Séparer de la réponse par Bram Vanroy )