CSS3 100vh tidak konstan di browser seluler

288

Saya memiliki masalah yang sangat aneh ... di setiap browser dan versi seluler yang saya temui perilaku ini:

  • semua browser memiliki menu teratas ketika Anda memuat halaman (menunjukkan bilah alamat misalnya) yang meluncur ke atas ketika Anda mulai menggulir halaman.
  • 100vh kadang - kadang dihitung hanya pada bagian yang terlihat dari viewport, jadi ketika bilah browser geser ke atas 100vh meningkat (dalam hal piksel)
  • semua tata letak ulang cat dan sesuaikan kembali karena dimensi telah berubah
  • efek gelisah yang buruk untuk pengalaman pengguna

Bagaimana bisa menghindari masalah ini? Ketika saya pertama kali mendengar tentang viewport-height saya sangat senang dan saya pikir saya bisa menggunakannya untuk blok ketinggian tetap daripada menggunakan javascript, tapi sekarang saya pikir satu-satunya cara untuk melakukan itu sebenarnya javascript dengan beberapa acara ukuran ...

Anda dapat melihat masalahnya di: situs sampel

Adakah yang bisa membantu saya dengan / menyarankan solusi CSS?


kode tes sederhana:

Nereo Costacurta
sumber
1
jika saya mengerti pertanyaannya dengan baik masalah yang Anda hadapi adalah di browser mobile tingginya lebih dari viewport hight terlihat .. benar?
Gaurav Aggarwal
Menarik, tidak pernah memperhatikan itu sebelumnya. Ini terutama gambar latar belakang yang terasa gelisah. Bagaimana kalau Anda menambahkan kira-kira transition: 0.5s, agar perubahannya tidak tiba-tiba?
C14L
@GauravAggarwal nggak, persis sebaliknya: ketinggian viewport yang sebenarnya lebih besar daripada yang disediakan oleh browser ketika bilah alamatnya terlihat ...
Nereo Costacurta
1
Karena pertanyaan saya menjadi populer, saya ingin memberikan 5 sen: tidak akan lebih pintar untuk mempertahankan ketinggian jendela nyata dan hanya geser ke atas menu bar? sepertinya tidak begitu sulit. Sebenarnya seharusnya lebih mudah ... jari ke atas -> bilah menu geser ke atas hingga tak terlihat, jari ke bawah -> bilah menu geser ke bawah hingga benar-benar terlihat ... semuanya bersamaan dengan tubuh tanpa efek penyesuaian ulang dan gelisah ...
Nereo Costacurta
4
Google memiliki beberapa info bagus tentang ini: developers.google.com/web/updates/2016/12/url-bar-resizing Anda dapat menggunakan 100% daripada 100vh JIKA Anda telah mengubah tinggi badan menjadi 100%
Benisburgers

Jawaban:

203

Sayangnya ini disengaja ...

Ini adalah masalah yang diketahui (setidaknya dalam safari mobile), yang disengaja, karena mencegah masalah lain. Benjamin Poulain membalas bug webkit :

Ini sepenuhnya disengaja. Kami membutuhkan sedikit usaha untuk mencapai efek ini. :)

Masalah dasarnya adalah ini: area yang terlihat berubah secara dinamis saat Anda menggulir. Jika kami memperbarui tinggi viewport CSS sesuai, kami perlu memperbarui tata letak selama gulir. Tidak hanya itu terlihat seperti omong kosong, tetapi melakukan hal itu pada 60 FPS secara praktis tidak mungkin dilakukan di sebagian besar halaman (60 FPS adalah framerate dasar pada iOS).

Sulit untuk menunjukkan kepada Anda bagian "mirip kotoran", tetapi bayangkan ketika Anda menggulir, konten bergerak dan apa yang Anda inginkan di layar terus bergeser.

Memperbarui ketinggian secara dinamis tidak berfungsi, kami memiliki beberapa pilihan: jatuhkan unit viewport di iOS, cocokkan dengan ukuran dokumen seperti sebelum iOS 8, gunakan ukuran tampilan kecil, gunakan ukuran tampilan besar.

Dari data yang kami miliki, menggunakan ukuran tampilan yang lebih besar adalah kompromi terbaik. Sebagian besar situs web menggunakan unit viewport tampak hebat sebagian besar waktu.

Nicolas Hoizey telah meneliti ini sedikit: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

Tidak ada perbaikan yang direncanakan

Pada titik ini, tidak banyak yang dapat Anda lakukan kecuali menahan diri dari menggunakan ketinggian viewport pada perangkat seluler. Chrome juga berubah menjadi ini di 2016:

nils
sumber
7
Ya, seperti yang saya pikirkan. Hanya solusi yang layak adalah solusi scripted. Dari yang saya tahu, $(window).height()tidak terpengaruh oleh bug ini, jadi saya akan pergi ke sana. Terima kasih!
Nereo Costacurta
3
Script adalah satu-satunya cara bagi saya, dan bekerja dengan sempurna.
captDaylight
461
Jawaban yang paling menyedihkan pernah.
Winnemucca
15
Karena Chrome versi 56 vh selalu dihitung seolah-olah bilah URL disembunyikan dan karenanya vh tidak bertambah pada gulir, kecuali untuk position:fixed. Ini mirip dengan implementasi di Safari. Baca selengkapnya: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia
4
Chrome terbaru tidak berubah vhatau <html> 100%tinggi setelah memuat awal. Ini sebenarnya bagus - karena tata letak meronta / reflow ketika URL Bar menyembunyikannya bisa terlihat mengerikan. Firefoxdan Edge Mobilemasih secara dinamis memperbarui vhdan <html>tinggi, menyebabkan banyak sobek dan mengubah ukuran semua komponen saat menggulir, terutama buruk jika menggunakan SVG untuk gambar latar belakang
Drenai
143

Anda dapat mencoba min-height: -webkit-fill-available;di css bukan 100vh. Itu harus dipecahkan

Saurabh Jain
sumber
20
Saya akan pergi min-height: 100vh;sebagai mundur di mana -webkit-fill-availabletidak didukung.
Tammy Tee
Wow Terimakasih!! Dengan ini saya tidak perlu "react-div-100vh" lagi!
Andres Elizondo
1
jawaban yang paling membantu!
Gauthier
@TammyTee benar, lebih baik tetap min-height: 100vh;di sana karena kalau tidak akan merusak browser seperti Firefox (setidaknya di desktop, iOS menggunakan webkit tidak peduli apa pun browser).
JoniVR
2
Ini adalah solusi yang sangat bagus, tetapi tampaknya telah berubah di iOS 13 - Saya melihat ruang ekstra di bagian bawah bagian pertama, tetapi berfungsi persis seperti yang diinginkan di Safari untuk iOS 12.x codepen.io/RwwL/pen / PowjvPq (Anda harus membayar ini kemudian melihatnya dalam mode debug CodePen di iOS untuk benar-benar melihat apa yang saya maksud).
RwwL
27

dalam aplikasi saya, saya melakukannya seperti itu (naskah dan kode pos bersarang, jadi ubah kode sesuai):

const appHeight = () => {
    const doc = document.documentElement
    doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()

di css Anda:

:root {
   --app-height: 100%;
}

html,
body {
    padding: 0;
    margin: 0;
    overflow: hidden;
    width: 100vw;
    height: 100vh;

    @media not all and (hover:hover) {
        height: var(--app-height);
    }
}

ini berfungsi setidaknya pada ponsel chrome dan ipad. Yang tidak berfungsi adalah ketika Anda menambahkan aplikasi ke homescreen di iOS dan mengubah orientasi beberapa kali - entah bagaimana tingkat zoom mengacaukan nilai innerHeight, saya mungkin memposting pembaruan jika saya menemukan solusi untuk itu.

Demo

Andreas Herd
sumber
2
Ini sebenarnya pendekatan yang fantastis untuk masalah yang sangat menjengkelkan.
Michael Giovanni Pumo
1
Saya ingin menambahkan peringatan bahwa "@media tidak semua dan (hover: hover) {" bukan cara antipeluru untuk mendeteksi peramban seluler. Jangan ragu untuk menggunakan sesuatu yang lain.
Andreas Herd
Saya sangat suka pendekatan ini. Satu komentar yang akan saya sampaikan adalah tentang penggunaan pendengar acara. Ini menyebabkan pengecatan ulang halaman dan saya hanya menggunakannya dalam skenario tertentu ketika itu sangat penting bagi pengguna untuk melihat viewport yang benar (yaitu tampilan awal halaman rumah)
Zach Gollwitzer
22

Untuk banyak situs yang saya bangun, klien akan meminta spanduk 100vh dan seperti yang Anda temukan, itu menghasilkan pengalaman "gelisah" yang buruk di ponsel saat Anda mulai menggulir. Inilah cara saya memecahkan masalah untuk pengalaman konsisten yang mulus di semua perangkat:

Saya pertama-tama mengatur CSS elemen banner saya height:100vh

Lalu saya menggunakan jQuery untuk mendapatkan ketinggian dalam piksel elemen banner saya dan menerapkan gaya inline menggunakan ketinggian ini.

var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });

Melakukan hal ini memecahkan masalah pada perangkat seluler seperti ketika halaman dimuat, elemen spanduk diatur ke 100vh menggunakan CSS dan kemudian jQuery mengesampingkan ini dengan meletakkan CSS sebaris pada elemen spanduk saya yang menghentikannya mengubah ukuran ketika pengguna mulai menggulir.

Namun, pada desktop jika pengguna mengubah ukuran jendela browser mereka, elemen banner saya tidak akan mengubah ukuran karena sekarang memiliki ketinggian yang ditetapkan dalam piksel karena jQuery di atas. Untuk mengatasinya, saya menggunakan Deteksi Seluler untuk menambahkan kelas 'seluler' ke badan dokumen saya. Dan kemudian saya bungkus jQuery di atas dalam pernyataan if:

if ($('body').hasClass('mobile')) {
  var viewportHeight = $('.banner').outerHeight();
  $('.banner').css({ height: viewportHeight });
}

Akibatnya, jika pengguna menggunakan perangkat seluler, kelas 'seluler' ada di badan laman saya dan jQuery di atas dijalankan. Jadi elemen spanduk saya hanya akan menerapkan CSS sebaris di perangkat seluler sementara di desktop aturan CSS 100vh asli tetap berlaku.


sumber
4
Saya akan men-tweak untuk menggunakannya $('.banner').css({ height: window.innerHeight });, yang merupakan "nyata" ketinggian viewport. Contoh bagaimana innerHeight bekerja
Martin
8
Yang document.querySelector('.banner').style.height = window.innerHeight;untuk orang yang menulis JavaScript yang sebenarnya.
Fabian von Ellerts
18

Lihat jawaban ini: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});
body {
  background-color: #333;
}

.module {
  height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
  margin: 0 auto;
  max-width: 30%;
}

.module__item {
  align-items: center;
  display: flex;
  height: 20%;
  justify-content: center;
}

.module__item:nth-child(odd) {
  background-color: #fff;
  color: #F73859;
}

.module__item:nth-child(even) {
  background-color: #F73859;
  color: #F1D08A;
}
<div class="module">
  <div class="module__item">20%</div>
  <div class="module__item">40%</div>
  <div class="module__item">60%</div>
  <div class="module__item">80%</div>
  <div class="module__item">100%</div>
</div>

manuel-84
sumber
2
solusi cerdas, saya menghadapi masalah dengan ketinggian viewport pada perangkat seluler, ini juga harus diterima sebagai solusi (khusus untuk perangkat seluler)
Julio Vedovatto
solusi yang sangat bagus. Hanya dalam kasus saya, saya tidak menggunakan ponyfill, tetapi lebih logis bahwa untuk semua situs desktop saya menggunakan 100vh, sedangkan untuk seluler saya menggunakan var (kami tidak memiliki IE11 di dunia seluler).
FrEaKmAn
Hanya solusi yang berfungsi untuk elemen bersarang. Pada orang lain yang harus mengganti beberapa ratus instance, Anda dapat (setidaknya sebagian besar waktu) cocok dengan regexp (-)? ([\ D.] +) Vh dan menggantinya dengan calc (var (- vh) * $ 1 $ 2)
ecc521
10

Saya datang dengan komponen Bereaksi - memeriksanya jika Anda menggunakan Bereaksi atau menelusuri kode sumber jika Anda tidak, sehingga Anda dapat menyesuaikannya dengan lingkungan Anda.

Ini mengatur tinggi div layar penuh untuk window.innerHeightdan kemudian memperbaruinya pada ukuran jendela.

Mikhail Vasin
sumber
3
Tidak semua pahlawan memakai topi. Anda baik-baik saja misalnya itu. Terima kasih banyak. menghemat waktu.
NarayaN
Dan untuk pecinta Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan
6

Ketika saya mencari solusi beberapa hari, inilah milik saya untuk semua orang yang menggunakan VueJS dengan Vuetify (solusi saya menggunakan v-app-bar, v-navigation-drawer dan v-footer): Saya membuat App.scss (digunakan di App. vue) dengan konten berikut:

.v-application {
    height: 100vh;
    height: -webkit-fill-available;
}

.v-application--wrap {
    min-height: 100vh !important;
    min-height: -webkit-fill-available !important;
}

Jürgen P.
sumber
1
Ini bekerja dengan baik, dan dapat digeneralisasi di luar dunia Vue / Vuetify.
leo
5

Bagi saya trik seperti itu berhasil:

height: calc(100vh - calc(100vh - 100%))
Patryk Janik
sumber
Apakah berbeda dengan tinggi: 100%?
FF_Dev
Ya, 100vh adalah 100% dari ketinggian viewport (perangkat Anda), ketika 100% murni hanya mengisi semua area induk yang tersedia (blok induk dapat memiliki tinggi mis. 50px dan hanya akan mengisi dengan ketinggian induk ini). Singkatnya besar.
Patryk Janik
4

@nils menjelaskannya dengan jelas.

Lalu apa selanjutnya?

Saya baru saja kembali menggunakan relatif 'klasik' %(persentase) di CSS.

Sering kali lebih banyak upaya untuk mengimplementasikan sesuatu daripada yang akan digunakan vh, tetapi setidaknya, Anda memiliki solusi yang cukup stabil yang bekerja di berbagai perangkat dan browser tanpa gangguan UI yang aneh.

klimat
sumber
1
Tinggi: 100% untuk spanduk masih melompati browser seluler. Saya telah mengujinya di Android, dan sejauh ini Chrome dan Opera Mini memberikan 100% tinggi VP yang terlihat, dan tidak berubah pada gulir. Edge dan Firefox mengubah tinggi 100%, dan mengecat ulang tampilan. Firefox sangat buruk, dengan sobekan, rerendered teks, sangat jelas menata ulang
Drenai
1
@ Donai Anda yakin menerapkan ini dengan benar? Bisakah Anda membuktikannya pada contoh kecil di jsfiddle.net atau lebih?
klimat
Ya, saya yakin :-)
Drenai
3

Saya baru saja menemukan aplikasi web yang saya rancang memiliki masalah ini dengan iPhone dan iPad, dan menemukan artikel yang menyarankan untuk menyelesaikannya menggunakan kueri media yang ditargetkan pada perangkat Apple tertentu.

Saya tidak tahu apakah saya dapat membagikan kode dari artikel itu di sini, tetapi alamatnya adalah ini: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Mengutip artikel: "hanya cocokkan tinggi elemen dengan tinggi perangkat menggunakan kueri media yang menargetkan versi iPhone dan iPad resolusi lama."

Mereka menambahkan hanya 6 permintaan media untuk mengadaptasi elemen ketinggian penuh, dan itu harus berfungsi karena sepenuhnya diterapkan CSS.

Sunting tertunda: Saya tidak dapat mengujinya sekarang, tetapi saya akan kembali dan melaporkan hasil saya.

Jahaziel
sumber
23
masih menunggu hasil itu g
gman
2
Maaf saya tidak kembali seperti yang dijanjikan. Tetapi setelah mengutak-atik HTML dan CSS lebih lama dari yang saya inginkan, saya mengubah desain tata letak saya dan menemukan cara yang bekerja "ok" untuk kebutuhan saya, tetapi bukan solusi universal.
Jahaziel
2

Karena saya baru, saya tidak dapat mengomentari jawaban lain.

Jika seseorang mencari jawaban untuk membuat ini berfungsi (dan dapat menggunakan javascript - karena tampaknya diperlukan untuk membuat ini berfungsi saat ini) pendekatan ini telah bekerja cukup baik untuk saya dan itu juga bertanggung jawab atas perubahan orientasi seluler. Saya menggunakan Jquery untuk kode contoh tetapi harus bisa dilakukan dengan vanillaJS.

-Pertama, saya menggunakan skrip untuk mendeteksi apakah perangkat itu menyentuh atau membawa. Contoh tanpa tulang:

if ("ontouchstart" in document.documentElement) {
    document.body.classList.add('touch-device');

} else {
    document.body.classList.add('hover-device');
}

Ini menambahkan kelas ke elemen tubuh sesuai dengan jenis perangkat (hover atau sentuh) yang dapat digunakan nanti untuk skrip ketinggian.

-Selanjutnya gunakan kode ini untuk mengatur ketinggian perangkat saat memuat dan perubahan orientasi:

if (jQuery('body').hasClass("touch-device")) {
//Loading height on touch-device
    function calcFullHeight() {
        jQuery('.hero-section').css("height", $(window).height());
    }

    (function($) {
        calcFullHeight();

        jQuery(window).on('orientationchange', function() {
            // 500ms timeout for getting the correct height after orientation change
            setTimeout(function() {
                calcFullHeight();
            }, 500);

        });
    })(jQuery);

} else {
    jQuery('.hero-section').css("height", "100vh");


}

-Timeout diatur sehingga perangkat akan menghitung ketinggian baru dengan benar pada perubahan orientasi. Jika tidak ada waktu habis, menurut pengalaman saya ketinggiannya tidak akan benar. 500 ms mungkin berlebihan tetapi telah bekerja untuk saya.

-100vh pada hover-devices adalah fallback jika browser menimpa CSS 100vh.

tjgoodman
sumber
2
Touchstart tidak didukung di semua browser, dan beberapa perangkat non-seluler memilikinya developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai
@Drenai Touchstart didukung di sebagian besar peramban seluler. Pada desktop, dukungannya sedikit kurang, IE, Opera dan Safari tidak mendukungnya. Namun pertanyaan aslinya ditujukan untuk seluler dan bukan desktop. Yang menjadikan ini jawaban yang valid.
Paul
Terima kasih atas komentarnya! Saya terutama menggunakan taktik ini untuk mengatasi pengaturan ulang dan kegembiraan layar pada beberapa peramban seluler. Jika browser tidak mendukung touchstart fallback akan menjadi css 100vh - jika 100vh tidak didukung itu topik yang berbeda. Jika perangkat desktop kebetulan memiliki dukungan sentuh maka ketinggian akan dihitung dengan javascript. Kami kemudian kehilangan perhitungan ulang pada ukuran yang disediakan 100vh, tapi itu bukan masalah fatal secara umum karena tidak ada perubahan orientasi pada desktop.
tjgoodman
Selain itu, perhitungan ketinggian juga bisa dilampirkan untuk mengubah ukuran acara tetapi kemudian kita dapat mengalami masalah yang sama persis penyesuaian ulang dan kegelisahan pada perangkat seluler. Karena itu, saya menemukan taktik ini praktis.
tjgoodman
1
@ Paul Jika browser desktop Chrome dan Firefox memiliki touchstart, maka tentu itu fitur yang salah untuk digunakan untuk mendeteksi browser mobile? Saya baru saja mengujinya di Chrome dan Firefox pada Windows, dan keduanya lulus tes ini
Drenai
2

Kode berikut menyelesaikan masalah (dengan jQuery).

var vhHeight = $("body").height();
var chromeNavbarHeight = vhHeight - window.innerHeight;
$('body').css({ height: window.innerHeight, marginTop: chromeNavbarHeight });

Dan elemen lainnya digunakan %sebagai unit untuk menggantikan vh.

Ray1422
sumber
2

Ini pekerjaan yang saya gunakan untuk aplikasi Bereaksi saya.

iPhone 11 Pro & iPhone Pro Max - 120px

iPhone 8 - 80px

max-height: calc(100vh - 120px);

Ini kompromi tetapi perbaikan yang relatif sederhana

Trevor Wood
sumber
1

Berikut ini bekerja untuk saya:

html { height: 100vh; }

body {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
}

/* this is the container you want to take the visible viewport  */
/* make sure this is top-level in body */
#your-app-container {
  height: 100%;
}

The bodyakan mengambil ketinggian viewport terlihat dan #your-app-containerdengan height: 100%akan membuat wadah mengambil ketinggian viewport terlihat.

Rico Kahler
sumber
0

Karena itu tidak akan diperbaiki, Anda dapat melakukan sesuatu seperti:

# html
<body>
  <div class="content">
    <!-- Your stuff here -->
  </div>
</body>

# css
.content {
  height: 80vh;
}

Bagi saya itu adalah solusi tercepat dan lebih murni daripada bermain dengan JavaScript yang tidak dapat berfungsi pada banyak perangkat dan browser.

Cukup gunakan nilai yang tepat vhyang sesuai dengan kebutuhan Anda.

turkus
sumber
0

Menggunakan vh pada perangkat seluler tidak akan berfungsi dengan 100vh, karena pilihan desain mereka menggunakan seluruh ketinggian perangkat tidak termasuk bilah alamat dll.

Jika Anda mencari tata letak termasuk ketinggian div sebanding dengan ketinggian tampilan sebenarnya, saya menggunakan solusi css murni berikut:

:root {
  --devHeight: 86vh; //*This value changes
}

.div{
    height: calc(var(--devHeight)*0.10); //change multiplier to suit required height
}

Anda memiliki dua opsi untuk mengatur ketinggian viewport, secara manual mengatur --devHeight ke ketinggian yang berfungsi (tetapi Anda harus memasukkan nilai ini untuk setiap jenis perangkat yang Anda kodekan)

atau

Gunakan javascript untuk mendapatkan ketinggian jendela dan kemudian perbarui --devheight tentang memuat dan menyegarkan viewport (namun ini memang mengharuskan menggunakan javascript dan bukan solusi css murni)

Setelah Anda mendapatkan ketinggian tampilan yang benar, Anda dapat membuat beberapa div pada persentase yang tepat dari total tinggi viewport dengan hanya mengubah pengali di setiap div yang Anda tetapkan ketinggiannya.

0,10 = 10% tinggi tampilan 0,57 = 57% tinggi tampilan

Semoga ini bisa membantu seseorang;)

Ray Andison
sumber
1
Itu solusi yang bagus. Anda dapat membaca lebih lanjut tentang ini di artikel ini tentang trik-Css .
Amaury Hanser
0

Anda dapat melakukan ini dengan menambahkan skrip dan gaya berikut

  function appHeight() {
    const doc = document.documentElement
    doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
  }
  window.addEventListener('resize', appHeight);
  appHeight();

Gaya

.module {
 height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}
Sarath Ak
sumber
0

Cobalah html, body { height: 100% }sesuatu dengan efek 100vh pada perangkat seluler.

Sam Bokai
sumber
-1

Anda dapat mencoba memberikan position: fixed; top: 0; bottom: 0;properti ke wadah Anda.

Upinion Hasan
sumber
2
Tambahkan lebih banyak info tentang mengapa ini adalah jawaban untuk pertanyaan itu.
Drake yang terinfeksi