Elemen yang dirotasi di CSS yang memengaruhi tinggi induknya dengan benar

119

Katakanlah saya memiliki beberapa kolom, yang beberapa di antaranya ingin saya putar nilainya:

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>

Dengan CSS ini:

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

Hasilnya akan terlihat seperti ini:

Rangkaian empat elemen level blok.  Teks elemen ketiga diputar.

Apakah mungkin untuk menulis CSS yang akan menyebabkan elemen yang dirotasi memengaruhi tinggi induknya, sehingga teks tidak akan tumpang tindih dengan elemen lainnya? Sesuatu seperti ini:

Teks kolom ketiga sekarang memengaruhi tinggi kotak induknya sehingga teks tersebut pas di dalam kotak.

Chris
sumber
7
mode penulisan sekarang tersedia untuk sebagian besar browser (digunakan untuk menjadi fitur IE5) yang akan saya lakukan sekarang jsfiddle.net/MTyFP/698 lihat: developer.mozilla.org/en-US/docs/Web/CSS/writing-mode
G-Cyrillus
1
@ G-Cyr writing-modeadalah tersedia untuk sebagian besar browser, tapi mengerikan kereta. Atas pertanyaan terkait, saya melewati beberapa iterasi untuk mencoba mengatasi bug khusus browser sebelum menyerah; Anda dapat melihat suksesi kegagalan saya di stackoverflow.com/posts/47857248/revision , tempat saya memulai dengan sesuatu yang berfungsi di Chrome tetapi tidak di Firefox atau Edge, kemudian merusak Chrome dalam proses memperbaiki dua lainnya, dan akhirnya mengembalikan menjawab dan memberi label sebagai khusus Chrome. Berhati-hatilah, jika Anda ingin menempuh jalan ini. (Perhatikan juga bahwa ini tidak ada gunanya dengan elemen non-teks.)
Mark Amery
1
mode penulisan afaik hanya bekerja pada teks ...
Fredrik Johansson

Jawaban:

51

Dengan asumsi Anda ingin memutar 90 derajat, hal ini dimungkinkan, bahkan untuk elemen non-teks - tetapi seperti banyak hal menarik lainnya di CSS, ini membutuhkan sedikit kecerdikan. Solusi saya juga secara teknis memanggil perilaku tidak terdefinisi sesuai dengan spesifikasi CSS 2 - jadi sementara saya telah menguji dan mengonfirmasi bahwa ini berfungsi di Chrome, Firefox, Safari, dan Edge, saya tidak dapat menjanjikan kepada Anda bahwa itu tidak akan rusak di masa mendatang rilis browser.

Jawaban singkat

Diberikan HTML seperti ini, dimana Anda ingin merotasi .element-to-rotate...

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

... perkenalkan dua elemen pembungkus di sekitar elemen yang ingin Anda putar:

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

... lalu gunakan CSS berikut, untuk memutar berlawanan arah jarum jam (atau lihat komentar transformuntuk cara mengubahnya menjadi rotasi searah jarum jam):

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

Demo cuplikan tumpukan

Bagaimana cara kerjanya?

Kebingungan dalam menghadapi mantra yang saya gunakan di atas adalah wajar; ada banyak hal yang terjadi, dan keseluruhan strategi tidak langsung dan membutuhkan pengetahuan tentang CSS trivia untuk memahaminya. Mari kita bahas langkah demi langkah.

Inti dari masalah yang kita hadapi adalah bahwa transformasi yang diterapkan ke elemen menggunakan transformproperti CSS -nya semuanya terjadi setelah tata letak dilakukan. Dengan kata lain, penggunaan transformpada elemen sama sekali tidak mempengaruhi ukuran atau posisi induknya atau elemen lainnya. Sama sekali tidak ada cara untuk mengubah fakta tentang cara transformkerja ini. Jadi, untuk membuat efek memutar elemen dan tinggi induknya mengikuti rotasi, kita perlu melakukan hal-hal berikut:

  1. Entah bagaimana membangun beberapa elemen lain yang tingginya sama dengan lebar.element-to-rotate
  2. Tuliskan transformasi kita .element-to-rotateseperti untuk melapisinya persis pada elemen dari langkah 1.

Elemen dari langkah 1 harus .rotation-wrapper-outer. Tapi bagaimana kita bisa menyebabkan nya tinggi untuk menjadi sama dengan .element-to-rotate's lebar ?

Komponen kunci dalam strategi kami di sini adalah padding: 50% 0aktif .rotation-wrapper-inner. Eksploitasi ini adalah detail eksentrik dari spesifikasi untukpadding : padding persentase itu, bahkan untuk padding-topdan padding-bottom, selalu ditentukan sebagai persentase lebar wadah elemen . Ini memungkinkan kami untuk melakukan trik dua langkah berikut:

  1. Kami menetapkan display: tablepada .rotation-wrapper-outer. Ini menyebabkannya memiliki lebar menyusut agar pas , yang berarti lebarnya akan diatur berdasarkan lebar intrinsik isinya - yaitu, berdasarkan lebar intrinsik .element-to-rotate. (Pada mendukung browser, kita bisa mencapai hal ini lebih bersih dengan width: max-content, tetapi sebagai Desember 2017, max-contentyang masih belum didukung di Ujung .)
  2. Kami mengatur heightof .rotation-wrapper-innerke 0, lalu mengatur paddingnya ke 50% 0(yaitu, 50% atas dan 50% bawah). Ini menyebabkannya mengambil ruang vertikal sama dengan 100% lebar induknya - yang, melalui trik di langkah 1, sama dengan lebarnya .element-to-rotate.

Selanjutnya, yang tersisa hanyalah melakukan rotasi dan pemosisian elemen turunan yang sebenarnya. Secara alami, transform: rotate(-90deg)melakukan rotasi; kita gunakan transform-origin: top left;untuk menyebabkan rotasi terjadi di sekitar sudut kiri atas elemen yang diputar, yang membuat terjemahan selanjutnya lebih mudah untuk dipikirkan, karena ia meninggalkan elemen yang diputar tepat di atas tempat yang seharusnya digambar. Kita kemudian dapat menggunakan translate(-100%)untuk menyeret elemen ke bawah dengan jarak yang sama dengan lebar pra-rotasinya.

Itu masih belum mendapatkan posisi yang benar, karena kita masih perlu mengimbangi untuk 50% padding atas .rotation-wrapper-outer. Kami mencapai itu dengan memastikan bahwa .element-to-rotatehas display: block(sehingga margin akan bekerja dengan baik di atasnya) dan kemudian menerapkan -50% margin-top- perhatikan bahwa persentase margin juga ditentukan relatif terhadap lebar elemen induk.

Dan itu dia!

Mengapa ini tidak sepenuhnya sesuai dengan spesifikasi?

Karena berikut catatan dari definisi persentase paddings dan margin di spec (bolding mine):

Persentase dihitung sehubungan dengan lebar blok berisi kotak yang dihasilkan , bahkan untuk 'padding-top' dan 'padding-bottom' . Jika lebar blok penampung bergantung pada elemen ini, maka tata letak yang dihasilkan tidak ditentukan di CSS 2.1.

Karena seluruh trik berputar di sekitar membuat bantalan elemen pembungkus bagian dalam menjadi relatif terhadap lebar wadahnya yang pada gilirannya bergantung pada lebar anaknya, kami mencapai kondisi ini dan menjalankan perilaku yang tidak ditentukan. Saat ini bekerja di semua 4 browser utama, meskipun - tidak seperti beberapa tweak yang tampaknya sesuai dengan spesifikasi untuk pendekatan yang saya coba, seperti mengubah .rotation-wrapper-innermenjadi saudara kandung .element-to-rotatealih-alih orang tua.

Mark Amery
sumber
1
Saya menerima ini sebagai jawaban untuk saat ini. The writing-modesolusi akan menjadi pendekatan yang terbaik jika IE 11 tidak perlu didukung.
Chris
1
Tangkapan penting: Tidak berfungsi untuk rotasi lain seperti 45deg.
E. Villiger
meskipun diterima, ini bukanlah jawaban benar yang mutakhir. Lihat di bawah untuk jawaban menggunakan mode menulis.
GilShalit
@ mark-amery, saya harus menyesuaikan CSS untuk kode saya: imgur.com/a/ZgafkgE Gambar pertama dengan CSS Anda, kedua dengan penyesuaian saya menghapus transformOrigin: 'kiri atas', & mengubah transformasi: 'translate (-100% ) ', untuk mentransformasikan:' 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 Pahit
44

Seperti yang ditunjukkan oleh komentar G-Cyr dengan benar, dukungan saat writing-modeini lebih dari layak . Dikombinasikan dengan rotasi sederhana, Anda mendapatkan hasil yang Anda inginkan. Lihat contoh di bawah ini.

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

Bram Vanroy
sumber
1
Senang saya menggulir ke bawah - ini menyelamatkan saya dari dunia kesakitan menggunakan rotasi dan terjemahan.
James McLaughlin
3
Untuk mendapatkan rotasi yang diminta dalam pertanyaan, gunakanwriting-mode: vertical-lr; transform: rotate(180deg);
James McLaughlin
41

Sayangnya (?) Ini adalah cara kerjanya meskipun Anda memutar elemen Anda, ia masih memiliki lebar dan tinggi tertentu, yang tidak berubah setelah rotasi. Anda mengubahnya secara visual, tetapi tidak ada kotak pembungkus yang tidak terlihat yang mengubah ukurannya saat Anda memutar sesuatu.

Bayangkan memutarnya kurang dari 90 ° (misalnya transform: rotate(45deg)): Anda harus memasukkan kotak tak terlihat yang sekarang memiliki dimensi ambigu berdasarkan dimensi asli dari objek yang Anda putar dan nilai rotasi sebenarnya.

Objek yang diputar

Tiba-tiba, Anda tidak hanya memiliki widthdan heightdari objek yang telah diputar, tetapi Anda juga memiliki widthdan heightdari "kotak tak terlihat" di sekitarnya. Bayangkan meminta lebar luar objek ini - apa yang akan dikembalikannya? Lebar benda, atau kotak baru kita? Bagaimana kita membedakan keduanya?

Oleh karena itu, tidak ada CSS yang dapat Anda tulis untuk memperbaiki perilaku ini (atau harus saya katakan, "otomatiskan"). Tentu saja Anda dapat meningkatkan ukuran penampung induk Anda dengan tangan, atau menulis beberapa JavaScript untuk menanganinya.

(Untuk memperjelas, Anda dapat mencoba menggunakan element.getBoundingClientRectuntuk mendapatkan persegi panjang yang disebutkan sebelumnya).

Seperti yang dijelaskan dalam spesifikasi :

Di namespace HTML, properti transform tidak memengaruhi aliran konten yang mengelilingi elemen yang diubah.

Ini berarti bahwa tidak ada perubahan yang akan dilakukan pada konten di sekitar objek yang Anda ubah, kecuali Anda melakukannya dengan tangan.

Satu-satunya hal yang diperhitungkan saat Anda mengubah objek adalah area luapan:

(...) luas area luapan memperhitungkan elemen yang diubah. Perilaku ini mirip dengan apa yang terjadi ketika elemen diimbangi melalui pemosisian relatif .

Lihat jsfiddle ini untuk mempelajari lebih lanjut.

Sebenarnya cukup bagus untuk membandingkan situasi ini dengan objek yang diimbangi menggunakan: position: relative- konten di sekitarnya tidak berubah, meskipun Anda sedang memindahkan objek ( contoh ).


Jika Anda ingin menangani ini menggunakan JavaScript, lihat pertanyaan ini .

MMM
sumber
8
Saya akan mengatakan bahwa dalam kerangka UI normal, lebar dan tinggi orang tua Anda harus dililitkan di sekitar lebar dan tinggi kotak pembatas anak yang terpengaruh seperti yang Anda sebutkan oleh rotasi anak. Lebar dan tinggi anak adalah lebar dan tinggi mereka sebelum rotasi dan lebar dan tinggi orang tua ditentukan oleh bbox anak. Itu sangat mendasar dan menurut saya CSS / HTML harus bekerja seperti itu ... Jika tidak ada yang akan mengajukan pertanyaan tentang topik ini yang hanya dapat diperbaiki dengan cara yang tidak memuaskan dengan peretasan.
pengguna18490
25

Gunakan persentase untuk paddingdan elemen pseudo untuk mendorong konten. Di JSFiddle saya membiarkan elemen semu merah untuk menunjukkannya dan Anda harus mengkompensasi pergeseran teks tetapi saya pikir itulah cara yang harus dilakukan.

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

Artikel tentang solusi ini dapat ditemukan di sini: http://kizu.ru/en/fun/rotated-text/

Litek
sumber
3
Masalah dengan solusi ini adalah bahwa elemen mempertahankan lebar aslinya, jadi dalam (misalnya) tabel, jika itu adalah sel terluas, itu akan mendorong kolom itu lebar dengan spasi kosong yang tidak perlu yang akan diambil jika teksnya belum diputar. Itu mengganggu.
Jez
@litek, Bisakah Anda melihat jsfiddle.net/MTyFP/7 ? Saya mengalami masalah yang hampir sama dengan gambar. Pertanyaan: stackoverflow.com/questions/31072959/…
Awlad Liton
11

Silakan lihat versi terbaru di jawaban saya yang lain . Jawaban ini tidak lagi valid dan tidak berfungsi lagi. Saya akan meninggalkannya di sini karena alasan sejarah.


Pertanyaan ini sudah cukup lama, tetapi dengan dukungan saat ini untuk .getBoundingClientRect()objek dan nilainya widthserta heightnilainya, dalam kombinasi dengan kemampuan untuk menggunakan metode rapi ini bersama-sama transform, saya pikir solusi saya harus disebutkan juga.

Lihat aksinya di sini . (Diuji di Chrome 42, FF 33 & 37.)

getBoundingClientRectmenghitung lebar dan tinggi kotak aktual dari elemen. Sederhananya, kita dapat mengulang semua elemen dan menyetel min-heightnya ke tinggi kotak sebenarnya dari anak-anaknya.

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

(Dengan beberapa modifikasi Anda dapat mengulang melalui anak-anak dan mencari tahu mana yang tertinggi dan mengatur tinggi induk ke anak tertinggi. Namun, saya memutuskan untuk membuat contoh lebih lugas. Anda juga dapat menambahkan bantalan induk ke tinggi jika Anda mau, atau Anda dapat menggunakan box-sizingnilai yang relevan di CSS.)

Namun, perhatikan bahwa saya menambahkan translatedan transform-originke CSS Anda untuk membuat pemosisian lebih fleksibel dan akurat.

transform: rotate(-90deg) translateX(-100%);
transform-origin: top left;
Bram Vanroy
sumber
Betulkah? Pertanyaan css dengan jawaban JQUERY dan Anda menyebutnya jawaban sempurna @MichaelLumbroso? Ini berfungsi ya, tetapi tidak perlu javascript atau seluruh pustaka js seperti jQuery
Nebulosar
1
@Nebulosar Penggalian yang bagus. Pada saat itu (sudah dua setengah tahun yang lalu!) Ini adalah satu-satunya jawaban yang tidak menimbulkan masalah dengan pemosisian atau elemen pseudo yang tidak berfungsi dengan baik. Untungnya, dukungan browser untuk beberapa properti telah meningkat. Silakan lihat hasil edit saya.
Bram Vanroy
2
Ini pada dasarnya adalah dua jawaban yang sama sekali tidak berhubungan dalam satu. Sangat sesuai dengan aturan untuk memposting banyak jawaban untuk satu pertanyaan; Saya pikir akan lebih baik jika solusi baru Anda yang ditambahkan pada tahun 2017 adalah jawaban baru, sehingga dua jawaban yang benar-benar terpisah dapat dipilih dan dikomentari secara terpisah.
Mark Amery
@MarkAy Anda benar. Saya mengedit pertanyaan dan menambahkan jawaban baru di sini .
Bram Vanroy
Saya dapat menggunakan jawaban ini dan acara pemuatan gambar untuk mendapatkan solusi yang berfungsi untuk gambar menggunakan: $ pic.on ('load', function () {$ pic.css ('min-height', $ pic [0] .getBoundingClientRect (). height);});
Miika L.
-18

Saya tahu ini adalah posting lama tetapi saya menemukannya saat berjuang dengan masalah yang persis sama. Solusi yang berhasil untuk saya adalah metode "berteknologi rendah" yang agak kasar dengan hanya mengelilingi div yang saya putar 90deg dengan banyak

<br>

Mengetahui perkiraan lebar (yang menjadi tinggi setelah rotasi) div, saya dapat mengkompensasi perbedaan dengan menambahkan br di sekitar div ini sehingga konten di atas dan di bawah didorong sesuai.

vic_sk
sumber
10
Mungkin ini berhasil, tetapi Anda tidak boleh melakukan ini.
MMM
Terima kasih atas tanggapan Anda! Ya, cara yang lebih baik saya menggunakan properti margin-top div untuk menyesuaikan tinggi dalam piksel saat objek diputar.
vic_sk