Elementi ruotati in CSS che influenzano correttamente l'altezza del genitore


119

Diciamo che ho un paio di colonne, di cui alcune vorrei ruotare i valori di:

http://jsfiddle.net/MTyFP/1/

<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>

Con questo CSS:

.statusColumn b {
  writing-mode: tb-rl;
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}

Finisce per assomigliare a questo:

Una serie di quattro elementi a livello di blocco.  Il testo del terzo elemento viene ruotato.

È possibile scrivere un CSS che farà sì che l'elemento ruotato influenzi l'altezza del suo genitore, in modo tale che il testo non si sovrapponga agli altri elementi? Qualcosa come questo:

Il testo della terza colonna ora influisce sull'altezza del riquadro principale in modo che il testo si adatti al riquadro.



7
la modalità di scrittura è ora disponibile per la maggior parte dei browser (era una funzionalità di IE5) Oggi farei jsfiddle.net/MTyFP/698 vedi: developer.mozilla.org/en-US/docs/Web/CSS/writing-mode
G-Cyrillus

1
@ G-Cyr writing-modeè disponibile per la maggior parte dei browser, ma è terribilmente buggato. Su una domanda correlata, sono passato attraverso più iterazioni cercando di aggirare i bug specifici del browser prima di arrendermi; puoi vedere la mia successione di errori su stackoverflow.com/posts/47857248/revisions , dove inizio con qualcosa che funziona in Chrome ma non Firefox o Edge, quindi interrompo Chrome nel processo di correzione degli altri due e finisco per ripristinare il mio rispondere e etichettarlo come solo Chrome. Sii cauto, se vuoi percorrere questa strada. (Si noti inoltre che non è utile con elementi non di testo.)
Mark Amery

1
La modalità di scrittura afaik funziona solo sul testo ...
Fredrik Johansson,

Risposte:


51

Supponendo che tu voglia ruotare di 90 gradi, questo è possibile, anche per elementi non di testo, ma come molte cose interessanti in CSS, richiede un po 'di astuzia. La mia soluzione inoltre richiama tecnicamente un comportamento indefinito secondo le specifiche CSS 2, quindi anche se ho testato e confermato che funziona su Chrome, Firefox, Safari e Edge, non posso prometterti che non si romperà in futuro versione del browser.

Risposta breve

Dato HTML come questo, dove vuoi ruotare .element-to-rotate...

<div id="container">
  <something class="element-to-rotate">bla bla bla</something>
</div>

... introduci due elementi wrapper attorno all'elemento che desideri ruotare:

<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>

... e quindi usa il seguente CSS, per ruotare in senso antiorario (o guarda il commento transformper un modo per cambiarlo in una rotazione in senso orario):

.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

Come funziona?

La confusione di fronte agli incantesimi che ho usato sopra è ragionevole; ci sono molte cose da fare e la strategia generale non è semplice e richiede una certa conoscenza delle curiosità CSS per essere capita. Vediamolo passo dopo passo.

Il nocciolo del problema che dobbiamo affrontare è che le trasformazioni applicate a un elemento utilizzando la sua transformproprietà CSS avvengono tutte dopo che il layout ha avuto luogo. In altre parole, l'utilizzo transformsu un elemento non influisce affatto sulle dimensioni o sulla posizione del suo genitore o di qualsiasi altro elemento. Non c'è assolutamente alcun modo per cambiare questo fatto di come transformfunziona. Quindi, per creare l'effetto di rotazione di un elemento e avere l'altezza del suo genitore rispetto alla rotazione, dobbiamo fare le seguenti cose:

  1. In qualche modo costruisci qualche altro elemento la cui altezza sia uguale alla larghezza del file.element-to-rotate
  2. Scrivi la nostra trasformazione su .element-to-rotatetale da sovrapporla esattamente all'elemento del passaggio 1.

L'elemento della fase 1 deve essere .rotation-wrapper-outer. Ma come possiamo causiamo la sua altezza sia uguale a .element-to-rotate's di larghezza ?

La componente chiave della nostra strategia qui è l' padding: 50% 0on .rotation-wrapper-inner. Questo exploit è un dettaglio eccentrico della specifica perpadding : che le imbottiture percentuali, anche per padding-tope padding-bottom, sono sempre definite come percentuali della larghezza del contenitore dell'elemento . Questo ci consente di eseguire il seguente trucco in due fasi:

  1. Abbiamo impostato display: tablesu .rotation-wrapper-outer. Ciò fa sì che abbia una larghezza di restringimento per adattarsi , il che significa che la sua larghezza verrà impostata in base alla larghezza intrinseca del suo contenuto, ovvero in base alla larghezza intrinseca di .element-to-rotate. (Sui browser che supportano, potremmo ottenere questo risultato in modo più pulito con width: max-content, ma a dicembre 2017 nonmax-content è ancora supportato in Edge .)
  2. Impostiamo heightof .rotation-wrapper-innersu 0, quindi impostiamo il suo riempimento su 50% 0(ovvero, 50% in alto e 50% in basso). Questo fa sì che occupi uno spazio verticale pari al 100% della larghezza del suo genitore - che, attraverso il trucco nel passaggio 1, è uguale alla larghezza di .element-to-rotate.

Quindi, non resta che eseguire la rotazione e il posizionamento effettivi dell'elemento figlio. Naturalmente, transform: rotate(-90deg)fa la rotazione; usiamo transform-origin: top left;per far sì che la rotazione avvenga attorno all'angolo in alto a sinistra dell'elemento ruotato, il che rende più facile ragionare la successiva traslazione, poiché lascia l'elemento ruotato direttamente sopra dove sarebbe stato altrimenti disegnato. Possiamo quindi utilizzare translate(-100%)per trascinare l'elemento verso il basso di una distanza pari alla sua larghezza di pre-rotazione.

Ciò non riesce ancora a ottenere il posizionamento corretto, perché dobbiamo ancora compensare per il riempimento superiore del 50% .rotation-wrapper-outer. Otteniamo che garantendo che .element-to-rotatepresenta display: block(in modo che i margini funzionerà correttamente su di esso) e quindi applicando una -50% margin-top- nota che i margini percentuali sono anche definiti relativamente alla larghezza dell'elemento genitore.

E questo è tutto!

Perché questo non è completamente conforme alle specifiche?

A causa della seguente nota dalla definizione di imbottiture percentuali e margini nella specifica (in grassetto mio):

La percentuale è calcolata rispetto alla larghezza del blocco contenitore del box generato , anche per 'padding-top' e 'padding-bottom' . Se la larghezza del blocco contenitore dipende da questo elemento, il layout risultante non è definito in CSS 2.1.

Poiché l'intero trucco ruotava attorno al rendere il riempimento dell'elemento di wrapper interno relativo alla larghezza del suo contenitore che a sua volta dipendeva dalla larghezza del suo figlio, stiamo colpendo questa condizione e invocando un comportamento non definito. Attualmente funziona in tutti e 4 i principali browser, tuttavia, a differenza di alcune modifiche all'approccio apparentemente conformi alle specifiche che ho provato, come cambiare .rotation-wrapper-innerper essere un fratello .element-to-rotateinvece che un genitore.


1
L'ho accettata come risposta per il momento. Le writing-modesoluzioni saranno l'approccio migliore se IE 11 non necessita di essere supportato.
Chris il

1
Notevole cattura: non funziona per altre rotazioni come 45deg.
E. Villiger

sebbene accettata, questa non è la risposta corretta aggiornata. Vedi sotto per le risposte usando la modalità di scrittura.
GilShalit

@ mark-amery, ho dovuto modificare il CSS per il mio codice: imgur.com/a/ZgafkgE La prima immagine con il tuo CSS, la seconda con le regolazioni Ho rimosso transformOrigin: "in alto a sinistra" e ho modificato la trasformazione: "translate (-100% ) ', per trasformare:' translate (20%) ', <div style = {{flexDirection:' column ', alignItems:' center ',}}> <div style = {{display:' table ', margin: 30 ,}}> <div style = {{padding: '50% 0 ', height: 0}}> <img src = {image} style = {{display:' block ', marginTop:' -50% ', transform : "rotate (-90deg) translate (20%)", maxWidth: 300, maxHeight: 400,}} /> </div> </div> <div> {title} </div> </div>
Dan Amaro

44

Come giustamente sottolinea il commento di G-Cyr , l' attuale supporto per writing-modeè più che decente . Combinato con una semplice rotazione, ottieni il risultato esatto che desideri. Vedi l'esempio sotto.

.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>


1
Sono contento di aver fatto scorrere verso il basso - questo mi ha salvato da un mondo di dolore usando rotazioni e traduzioni.
James McLaughlin

3
Per ottenere la rotazione richiesta nella domanda, utilizzarewriting-mode: vertical-lr; transform: rotate(180deg);
James McLaughlin

41

Sfortunatamente (?) È così che dovrebbe funzionare anche se ruoti il ​​tuo elemento ha ancora una certa larghezza e altezza, che non cambia dopo la rotazione. Lo si modifica visivamente, ma non esiste una scatola di avvolgimento invisibile che cambia le sue dimensioni quando si ruotano le cose.

Immagina di ruotarlo meno di 90 ° (es. transform: rotate(45deg)): Dovresti introdurre tale scatola invisibile che ora ha dimensioni ambigue in base alle dimensioni originali dell'oggetto che stai ruotando e al valore di rotazione effettivo.

Oggetto ruotato

All'improvviso, non hai solo il widthe heightdell'oggetto che hai ruotato, ma hai anche il widthe heightdella "scatola invisibile" attorno ad esso. Immagina di richiedere la larghezza esterna di questo oggetto: cosa restituirebbe? La larghezza dell'oggetto o la nostra nuova scatola? Come potremmo distinguere tra entrambi?

Pertanto, non esiste alcun CSS che puoi scrivere per correggere questo comportamento (o dovrei dire, "automatizzarlo"). Ovviamente puoi aumentare manualmente le dimensioni del tuo contenitore genitore o scrivere un po 'di JavaScript per gestirlo.

(Per essere chiari, puoi provare a utilizzare element.getBoundingClientRectper ottenere il rettangolo menzionato prima).

Come descritto nelle specifiche :

Nello spazio dei nomi HTML, la proprietà di trasformazione non influisce sul flusso del contenuto che circonda l'elemento trasformato.

Ciò significa che non verranno apportate modifiche al contenuto che circonda l'oggetto che stai trasformando, a meno che tu non le faccia a mano.

L'unica cosa che viene presa in considerazione quando trasformi il tuo oggetto è l'area di overflow:

(...) l'estensione dell'area di overflow tiene conto degli elementi trasformati. Questo comportamento è simile a ciò che accade quando gli elementi vengono spostati tramite posizionamento relativo .

Vedi questo jsfiddle per saperne di più.

In realtà è abbastanza buono confrontare questa situazione con l'offset di un oggetto usando: position: relative- il contenuto circostante non cambia, anche se stai spostando l'oggetto ( esempio ).


Se vuoi gestirlo utilizzando JavaScript, dai un'occhiata a questa domanda.


8
Direi che in un normale framework dell'interfaccia utente la larghezza e l'altezza dei tuoi genitori dovrebbero essere avvolte attorno alla larghezza e all'altezza del riquadro di delimitazione del bambino che è influenzato, come hai detto, dalle rotazioni del bambino. La larghezza e l'altezza dei bambini è la loro larghezza e altezza prima della rotazione e la larghezza e l'altezza dei genitori è definita dalla casella dei bambini. Questo è molto semplice e direi che CSS / HTML dovrebbe funzionare in questo modo ... Se non fosse nessuno farebbe domande su questo argomento che può essere risolto solo in modi insoddisfacenti con gli hack.
user18490

25

Usa percentuali per paddinge uno pseudo elemento per inviare il contenuto. In JSFiddle ho lasciato lo pseudo elemento rosso per mostrarlo e dovrai compensare lo spostamento del testo, ma penso che sia la strada da percorrere.

.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>

http://jsfiddle.net/MTyFP/7/

Una descrizione di questa soluzione può essere trovata qui: http://kizu.ru/en/fun/rotated-text/


3
Il problema con questa soluzione è che l'elemento mantiene la sua larghezza originale, quindi in (per esempio) una tabella, se fosse la cella più larga, spingerebbe quella colonna fuori con spazi bianchi non necessari che sarebbero stati occupati se il testo avesse non è stato ruotato. Questo è fastidioso.
Jez

@litek, potresti dare un'occhiata a jsfiddle.net/MTyFP/7 ? Ho quasi lo stesso problema con l'immagine. Domanda: stackoverflow.com/questions/31072959/...
Awlad Liton

11

Si prega di vedere la versione aggiornata nella mia altra risposta . Questa risposta non è più valida e semplicemente non funziona più. Lo lascio qui per ragioni storiche.


Questa domanda è abbastanza vecchio, ma con il supporto corrente per l' .getBoundingClientRect()oggetto e le sue widthe heightvalori, in combinazione con la possibilità di utilizzare questo metodo accurato insieme transform, penso che la mia soluzione deve essere menzionato come bene.

Guardalo in azione qui . (Testato in Chrome 42, FF 33 e 37.)

getBoundingClientRectcalcola la larghezza e l'altezza effettive della scatola dell'elemento. Molto semplicemente, quindi, possiamo scorrere tutti gli elementi e impostare la sua altezza minima sull'effettiva altezza della scatola dei loro figli.

$(".statusColumn").each(function() {
    var $this = $(this),
        child = $this.children(":first");
    $this.css("minHeight", function() {
        return child[0].getBoundingClientRect().height;
    });
});

(Con qualche modifica puoi scorrere i bambini e scoprire qual è il più alto e impostare l'altezza del genitore sul figlio più alto. Tuttavia, ho deciso di rendere l'esempio più semplice. Puoi anche aggiungere il riempimento del genitore al se lo desideri, oppure puoi utilizzare un box-sizingvalore pertinente in CSS.)

Nota, però, che ho aggiunto un translatee transform-original tuo CSS per rendere il posizionamento più flessibile e preciso.

transform: rotate(-90deg) translateX(-100%);
transform-origin: top left;

Veramente? Una domanda CSS con una risposta JQUERY e tu la chiami una risposta perfetta @MichaelLumbroso? Funziona sì, ma non è necessario JavaScript o un'intera libreria js come jQuery
Nebulosar

1
@Nebulosar Bello scavo. All'epoca (già due anni e mezzo fa!) Questa era l'unica risposta che non causava problemi di posizionamento o pseudo elementi non funzionanti bene. Fortunatamente, il supporto del browser per alcune proprietà è migliorato. Si prega di vedere la mia modifica.
Bram Vanroy

2
Si tratta essenzialmente di due risposte completamente indipendenti in una. È perfettamente conforme alle regole pubblicare più risposte a una domanda; Penso che sarebbe stato preferibile se la tua nuova soluzione aggiunta nel 2017 fosse stata una nuova risposta, in modo che le due risposte completamente separate potessero essere votate e commentate separatamente.
Mark Amery

@MarkAmery Hai ragione. Ho modificato la domanda e aggiunto una nuova risposta qui .
Bram Vanroy

Sono stato in grado di utilizzare questa risposta e l'evento di caricamento dell'immagine per ottenere una soluzione funzionante per le immagini utilizzando: $ pic.on ('load', function () {$ pic.css ('min-height', $ pic [0] .getBoundingClientRect (). height);});
Miika L.

-18

So che è un vecchio post ma l'ho trovato mentre lottavo esattamente con lo stesso problema. La soluzione che funziona per me è un metodo "low-tech" piuttosto rozzo che circonda semplicemente il div che ruoto di 90deg con molto

<br>

Conoscendo la larghezza approssimativa (che diventa altezza dopo la rotazione) di div, posso compensare la differenza aggiungendo br attorno a questo div in modo che il contenuto sopra e sotto venga spinto di conseguenza.


10
Forse funziona ma non dovresti mai farlo.
MMM

Grazie per il tuo feedback! Sì, un modo migliore per utilizzare la proprietà margin-top di div per regolare l'altezza in pixel quando l'oggetto viene ruotato.
vic_sk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.