CSS3 100vh nie jest stały w przeglądarce mobilnej


288

Mam bardzo dziwny problem ... w każdej przeglądarce i wersji mobilnej napotkałem takie zachowanie:

  • wszystkie przeglądarki mają górne menu po załadowaniu strony (na przykład pasek adresu), które przesuwają się w górę po rozpoczęciu przewijania strony.
  • 100vh czasami jest obliczane tylko na widocznej części rzutni, więc gdy pasek przeglądarki przesuwa się w górę, 100vh zwiększa się (pod względem pikseli)
  • cały układ ponownie maluje i dostosowuje od czasu zmiany wymiarów
  • zły efekt skakania dla wygody użytkownika

Jak można uniknąć tego problemu? Kiedy po raz pierwszy usłyszałem o wysokości rzutni, byłem podekscytowany i pomyślałem, że mogę użyć jej do bloków o stałej wysokości zamiast javascript, ale teraz myślę, że jedynym sposobem na to jest w rzeczywistości javascript z pewnym zdarzeniem zmiany rozmiaru ...

problem można zobaczyć na: przykładowej stronie

Czy ktoś może mi pomóc z / sugerować rozwiązanie CSS?


prosty kod testowy:


1
jeśli dobrze zrozumiałem pytanie, problem, z którym się borykasz, dotyczy przeglądarki mobilnej, wysokość jest większa niż widoczna wysokość rzutni .. prawda?
Gaurav Aggarwal

Ciekawe, nigdy wcześniej tego nie zauważyłem. Jest to przede wszystkim obraz tła, który jest wyraźnie skoczny. Co powiesz na dodanie transition: 0.5sczegoś takiego, aby zmiana była mniej gwałtowna?
C14L

@GauravAggarwal nie, dokładnie odwrotnie: rzeczywista wysokość rzutni jest większa niż ta zapewniana przez przeglądarkę, gdy jej pasek adresu jest widoczny ...
Nereo Costacurta

1
Ponieważ moje pytanie staje się popularne, chciałbym dać moje 5 centów: czy nie byłbym bardziej inteligentny, aby utrzymać rzeczywistą wysokość okna i tylko przesuwać pasek menu? to nie wydaje się takie trudne. W rzeczywistości powinno być łatwiejsze ... palec w górę -> Pasek menu slajdów aż niewidzialny palec w dół -> Pasek menu slajdów, aż całkowicie widoczny ... wszystko łącznie z ciałem bez ponownego uruchamiania i jumpy efektu ...
Nereo Costacurta

4
Google ma kilka dobrych informacji na ten temat: developers.google.com/web/updates/2016/12/url-bar-resizing Możesz użyć 100% zamiast 100vh JEŚLI zmieniłeś wysokość ciała na 100%
Benisburgers

Odpowiedzi:


203

Niestety jest to celowe…

Jest to dobrze znany problem (przynajmniej w safari na urządzeniach mobilnych), który jest celowy, ponieważ zapobiega innym problemom. Benjamin Poulain odpowiedział na błąd w pakiecie internetowym :

To jest całkowicie celowe. Osiągnięcie tego efektu wymagało z naszej strony sporo pracy. :)

Podstawowy problem jest następujący: widoczny obszar zmienia się dynamicznie podczas przewijania. Jeśli odpowiednio zaktualizujemy wysokość rzutni CSS, musimy zaktualizować układ podczas przewijania. Nie tylko to wygląda jak gówno, ale robienie tego przy 60 FPS jest praktycznie niemożliwe na większości stron (60 FPS to podstawowa liczba klatek na sekundę w iOS).

Trudno pokazać ci część „wygląda jak gówno”, ale wyobraź sobie, że podczas przewijania zawartość się przesuwa, a to, czego chcesz na ekranie, ciągle się zmienia.

Dynamiczna aktualizacja wysokości nie działała, mieliśmy kilka możliwości: upuść jednostki rzutni na iOS, dopasuj rozmiar dokumentu jak przed iOS 8, użyj małego rozmiaru widoku, użyj dużego rozmiaru widoku.

Z danych, które mieliśmy, najlepszym rozwiązaniem był użycie większego rozmiaru widoku. Większość stron internetowych korzystających z jednostek widoku wyglądała świetnie przez większość czasu.

Nicolas Hoizey dość dokładnie to zbadał: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

Brak zaplanowanej poprawki

W tym momencie niewiele można zrobić poza powstrzymaniem się od używania wysokości rzutni na urządzeniach mobilnych. Chrome zmienił się na to również w 2016 roku:


7
Tak, jak myślę. Jedynym realnym rozwiązaniem jest rozwiązanie skryptowe. Z tego co wiem, $(window).height()ten błąd nie ma wpływu, więc pójdę tą drogą. Dziękuję Ci!
Nereo Costacurta

3
Skrypt był dla mnie jedynym sposobem i działał idealnie.
captDaylight

458
Najbardziej przygnębiająca odpowiedź.
Winnemucca,

15
Od wersji 56 Chrome wartość vh jest zawsze obliczana tak, jakby pasek adresu URL był ukryty, a zatem wartość vh nie zwiększa się podczas przewijania, z wyjątkiem position:fixed. Jest to podobne do implementacji w Safari. Czytaj więcej: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia

4
Najnowszy Chrome nie zmienia się vhani nie zmienia <html> 100%wysokości po pierwszym załadowaniu. To jest naprawdę świetne - jako przebijanie układu / przepuszczanie, gdy ukrywa się pasek adresu URL, może wyglądać okropnie. Firefoxi Edge Mobilewciąż dynamicznie aktualizują się vhi <html>zwiększają wysokość, powodując wiele odrywania i zmiany rozmiaru wszystkich elementów podczas przewijania, szczególnie złe, jeśli używamy plików SVG do obrazów tła
Drenai

143

Możesz spróbować min-height: -webkit-fill-available;w swoim css zamiast 100vh. To powinno zostać rozwiązane


20
Odszedłbym min-height: 100vh;jako rezerwowy, gdzie -webkit-fill-availablenie jest obsługiwany.
Tammy Tee,

Wow, dziękuję !! Dzięki temu nie potrzebuję już „reaguj-dział-100vh”!
Andres Elizondo

1
najbardziej pomocna odpowiedź!
Gauthier

@TammyTee ma rację, lepiej jest min-height: 100vh;tam pozostać, ponieważ w przeciwnym razie zepsuje się w przeglądarkach takich jak Firefox (przynajmniej na komputerze stacjonarnym, iOS używa webkita bez względu na przeglądarkę).
JoniVR

2
To było bardzo miłe rozwiązanie, ale wydaje się, że zmieniło się w iOS 13 - Widzę dodatkowe miejsce na dole pierwszej sekcji, ale działa dokładnie tak, jak pożądane w Safari dla iOS 12.x codepen.io/RwwL/pen / PowjvPq (trzeba by to rozwidlić, a następnie wyświetlić w trybie debugowania CodePen na iOS, aby naprawdę zobaczyć, co mam na myśli).
RwwL,

27

w mojej aplikacji robię to tak (maszynopis i zagnieżdżone postcss, więc odpowiednio zmień kod):

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

w twoim css:

: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);
    }
}

działa przynajmniej na Chrome Mobile i iPadzie. To, co nie działa, to dodawanie aplikacji do ekranu głównego w systemie iOS i kilkakrotna zmiana orientacji - w jakiś sposób poziomy powiększenia psują się z wartością innerHeight, mogę opublikować aktualizację, jeśli znajdę rozwiązanie.

Próbny


2
To jest naprawdę fantastyczne podejście do bardzo irytującego problemu.
Michael Giovanni Pumo

1
Chciałbym dodać ostrzeżenie, że „@media nie wszystkie i (hover: hover) {” nie jest kuloodpornym sposobem wykrywania przeglądarek mobilnych. Możesz użyć czegoś innego.
Andreas Herd,

Bardzo podoba mi się to podejście. Jeden komentarz, który chciałbym napisać, dotyczy użycia detektora zdarzeń. Powoduje to ponowne malowanie strony i używam jej tylko w niektórych scenariuszach, gdy jest absolutnie niezbędne, aby użytkownik zobaczył poprawną rzutnię (tj. Początkowy widok strony głównej)
Zach Gollwitzer

22

W przypadku wielu witryn, które buduję, klient poprosi o baner 100vh, a jak już odkryłeś, powoduje to złe wrażenia na telefonie komórkowym, gdy zaczynasz przewijać. W ten sposób rozwiązuję problem, zapewniając płynne i spójne działanie na wszystkich urządzeniach:

Najpierw ustawiłem element banner CSS na height:100vh

Następnie używam jQuery, aby uzyskać wysokość mojego elementu banera w pikselach i stosuję styl wbudowany przy użyciu tej wysokości.

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

W ten sposób rozwiązuje się problem na urządzeniach mobilnych, gdy podczas ładowania strony element banner jest ustawiany na 100vh przy użyciu CSS, a następnie jQuery zastępuje to, umieszczając wbudowany CSS na moim elemencie banner, który powstrzymuje jego zmianę rozmiaru, gdy użytkownik zaczyna przewijać.

Jednak na pulpicie, jeśli użytkownik zmieni rozmiar okna przeglądarki, mój baner nie zmieni rozmiaru, ponieważ ma teraz stałą wysokość ustawioną w pikselach z powodu powyższego jQuery. Aby rozwiązać ten problem, używam Mobile Detect, aby dodać klasę „mobile” do treści mojego dokumentu. A potem zawijam powyższe jQuery w instrukcji if:

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

W rezultacie, jeśli użytkownik jest na urządzeniu mobilnym, klasa „mobile” jest obecna na treści mojej strony i powyższe polecenie jQuery jest wykonywane. Tak więc mój element transparentu zastosuje tylko wbudowany CSS na urządzeniach mobilnych, podczas gdy na komputerze oryginalna reguła 100vh CSS pozostaje na swoim miejscu.


4
Zmienię go, aby go używał $('.banner').css({ height: window.innerHeight });, czyli „prawdziwą” wysokość rzutni. Przykład działania innerHeight
Martin

8
Który jest document.querySelector('.banner').style.height = window.innerHeight;dla osób piszących rzeczywisty JavaScript.
Fabian von Ellerts,

18

Spójrz na tę odpowiedź: 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>


2
sprytne rozwiązanie, miałem problem z wysokością ekranu na urządzeniach mobilnych, to też należy zaakceptować jako rozwiązanie (specjalnie na urządzenia mobilne)
Julio Vedovatto

bardzo fajne rozwiązanie. W moim przypadku nie użyłem ponyfill, ale logikę, że we wszystkich witrynach na komputery używam 100vh, podczas gdy na urządzeniach mobilnych używam var (nie mamy IE11 w świecie mobilnym).
FrEaKmAn

Jedyne rozwiązanie, które działało dla elementów zagnieżdżonych. W kimkolwiek innym trzeba wymienić kilkaset instancji, możesz (przynajmniej przez większość czasu) dopasować wyrażenie regularne (-)? ([\ D.] +) Vh i zastąpić je calc (var (- vh) * 1 USD 2 USD)
ecc521

10

Wymyśliłem komponent React - sprawdź, czy używasz React lub przejrzyj kod źródłowy, jeśli nie, abyś mógł go dostosować do swojego środowiska.

Ustawia wysokość div pełnego ekranu na, window.innerHeighta następnie aktualizuje ją przy zmianie rozmiaru okna.


3
Nie wszyscy bohaterowie noszą czapki. Nic ci nie jest. Dziękuję Ci bardzo. zaoszczędzone godziny czasu.
NarayaN,

A dla miłośników Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan

6

Gdy szukałem rozwiązania kilka dni, tutaj jest moje dla wszystkich korzystających z VueJS z Vuetify (moje rozwiązanie używa paska aplikacji, szuflady nawigacji i stopki): stworzyłem App.scss (używany w aplikacji. vue) o następującej treści:

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

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


1
Działa to dobrze i można je uogólnić poza światem Vue / Vuetify.
Leo

5

Dla mnie taka sztuczka zadziałała:

height: calc(100vh - calc(100vh - 100%))

Czy różni się od wysokości: 100%?
FF_Dev

Tak, 100vh to 100% wysokości rzutni (twoje urządzenie), gdy czyste 100% wypełnia tylko cały dostępny obszar nadrzędny (blok nadrzędny może mieć np. Wysokość 50 pikseli i wypełnia tylko tę wysokość nadrzędną). Krótko mówiąc.
Patryk Janik

4

@ nils wyjaśnił to jasno.

Co dalej?

Właśnie wróciłem do używania względnego „klasycznego” %(procentowego) w CSS.

Często potrzeba więcej wysiłku, aby zaimplementować coś, niż byłoby to potrzebne vh, ale przynajmniej masz dość stabilne rozwiązanie, które działa na różnych urządzeniach i przeglądarkach bez dziwnych usterek interfejsu użytkownika.


1
Wysokość: 100% dla banerów nadal skacze w przeglądarkach mobilnych. Testowałem to na Androidzie i jak dotąd Chrome i Opera Mini dają 100% widocznej wysokości VP i nie zmieniają się podczas przewijania. Edge i Firefox zmieniają wysokość 100% i odmalowują widok. Firefox jest szczególnie zły, z łzawieniem, tekstem ponownie wysłanym, bardzo oczywistym układem
Drenai

1
@Drenai czy na pewno wdrażasz to poprawnie? Czy możesz to udowodnić na małym przykładzie na stronie jsfiddle.net?
klimat

Tak, jestem pewien :-)
Drenai

3

Właśnie znalazłem zaprojektowaną przeze mnie aplikację internetową, która ma ten problem z iPhone'ami i iPadami, i znalazłem artykuł sugerujący rozwiązanie tego problemu za pomocą zapytań o media skierowanych do określonych urządzeń Apple.

Nie wiem, czy mogę udostępnić kod z tego artykułu tutaj, ale adres jest następujący: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Cytując artykuł: „po prostu dopasuj wysokość elementu do wysokości urządzenia za pomocą zapytań o media kierowanych na starsze wersje rozdzielczości iPhone'a i iPada”.

Dodali tylko 6 zapytań o media, aby dostosować elementy o pełnej wysokości, i powinno działać, ponieważ jest w pełni zaimplementowane w CSS.

Edycja w toku: Nie mogę teraz go przetestować, ale wrócę i przedstawię wyniki.


23
wciąż czekam na te wyniki g
gman

2
Przepraszam, że nie wróciłem zgodnie z obietnicą. Ale po majstrowaniu przy HTML i CSS o wiele dłużej niż chciałbym, zmieniłem projekt mojego układu i znalazłem sposób, który działał „ok” dla moich potrzeb, ale nie był uniwersalnym rozwiązaniem.
Jahaziel

2

Ponieważ jestem nowy, nie mogę komentować innych odpowiedzi.

Jeśli ktoś szuka odpowiedzi, aby to zadziałało (i może korzystać z javascript - ponieważ wydaje się, że jest to w tej chwili wymagane), to podejście zadziałało dla mnie całkiem dobrze i uwzględnia również zmianę orientacji mobilnej. Używam Jquery dla przykładowego kodu, ale powinno być wykonalne z vanillaJS.

-Po pierwsze, używam skryptu, aby wykryć, czy urządzenie jest dotykane, czy unosi się. Przykład gołych kości:

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

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

Powoduje to dodanie klasy do elementu body zgodnie z typem urządzenia (najechanie myszą lub dotyk), którego można później użyć w skrypcie wysokości.

-Następnie użyj tego kodu, aby ustawić wysokość urządzenia przy obciążeniu i przy zmianie orientacji:

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 jest ustawiony tak, aby urządzenie poprawnie obliczało nową wysokość po zmianie orientacji. Jeśli nie ma limitu czasu, z mojego doświadczenia wynika, że ​​wysokość nie będzie poprawna. 500 ms może być przesadzeniem, ale zadziałało dla mnie.

Opcja -100vh na urządzeniach hover jest rezerwowa, jeśli przeglądarka zastępuje CSS 100vh.


2
Touchstart nie jest obsługiwany we wszystkich przeglądarkach, a niektóre urządzenia inne niż mobilne mają go developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai

@Drenai Touchstart jest obsługiwany w większości przeglądarek mobilnych. Na komputerach obsługa jest nieco mniejsza, IE, Opera i Safari nie obsługują jej. Jednak pierwotne pytanie dotyczy telefonu komórkowego, a nie komputera. Co sprawia, że ​​jest to poprawna odpowiedź.
Paul,

Dzięki za komentarze! Taką taktykę wykorzystałem głównie w celu przezwyciężenia ponownego dostosowania i przeskakiwania ekranu w niektórych przeglądarkach mobilnych. Jeśli przeglądarka nie obsługuje funkcji TouchStart, nastąpi powrót do css 100vh - jeśli 100vh nie jest obsługiwane, to inny temat. Jeśli urządzenie stacjonarne ma obsługę dotykową, wysokość zostanie obliczona za pomocą javascript. Następnie tracimy ponowne obliczenia przy zmianie rozmiaru zapewnianej przez 100vh, ale ogólnie nie jest to fatalny problem, ponieważ nie ma żadnych zmian orientacji na pulpicie.
tjgoodman,

Można również dołączyć obliczenie wysokości, aby zmienić rozmiar zdarzenia, ale wtedy możemy napotkać ten sam dokładny problem ponownego dostosowania i skoku na urządzeniach mobilnych. Dlatego uważam tę taktykę za praktyczną.
tjgoodman,

1
@Paul Jeśli przeglądarki stacjonarne Chrome i Firefox mają funkcję TouchStart, to z pewnością jest to niewłaściwa funkcja do wykrywania przeglądarki mobilnej? Właśnie przetestowałem to w Chrome i Firefox na Windowsie i oba zdałem ten test
Drenai

2

Poniższy kod rozwiązał problem (z jQuery).

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

Pozostałe elementy wykorzystują %jako jednostkę do wymiany vh.


2

Oto obejście, którego użyłem w mojej aplikacji React.

iPhone 11 Pro i iPhone Pro Max - 120 pikseli

iPhone 8 - 80px

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

Jest to kompromis, ale stosunkowo prosta poprawka


1

Dla mnie działało:

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

bodyZajmie widoczną wysokość rzutni oraz #your-app-containerz height: 100%sprawi, że pojemnik ma widoczną wysokość rzutni.



0

Ponieważ nie zostanie to naprawione, możesz zrobić coś takiego:

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

# css
.content {
  height: 80vh;
}

Dla mnie było to najszybsze i czystsze rozwiązanie niż gra w JavaScript, która nie mogła działać na wielu urządzeniach i przeglądarkach.

Wystarczy użyć odpowiedniej wartości, vhktóra odpowiada Twoim potrzebom.


0

Korzystanie z vh na urządzeniach mobilnych nie będzie działać z 100vh, ze względu na ich wybór projektu przy użyciu całej wysokości urządzenia, bez pasków adresu itp.

Jeśli szukasz układu zawierającego wysokości div proporcjonalne do rzeczywistej wysokości widoku, używam następującego rozwiązania czystego css:

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

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

Masz dwie opcje ustawiania wysokości rzutni, ręcznie ustaw --devHeight na wysokość, która działa (ale musisz wprowadzić tę wartość dla każdego typu urządzenia, dla którego kodujesz)

lub

Użyj javascript, aby uzyskać wysokość okna, a następnie zaktualizuj --devheight po załadowaniu i odświeżeniu okienka ekranu (wymaga to jednak użycia javascript i nie jest czystym rozwiązaniem css)

Po uzyskaniu prawidłowej wysokości widoku można utworzyć wiele elementów div z dokładnym procentem całkowitej wysokości rzutni, po prostu zmieniając mnożnik dla każdego elementu div, do którego przypisano wysokość.

0,10 = 10% wysokości widoku 0,57 = 57% wysokości widoku

Mam nadzieję, że to może komuś pomóc;)


1
To miłe rozwiązanie. Możesz przeczytać więcej na ten temat w tym artykule na temat sztuczek CSS .
Amaury Hanser,

0

Możesz to zrobić, dodając następujący skrypt i styl

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

Styl

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

0

Spróbuj html, body { height: 100% }czegoś z efektem 100vh na urządzeniach mobilnych.


-1

Możesz spróbować nadać position: fixed; top: 0; bottom: 0;właściwości swojemu kontenerowi.


2
Dodaj więcej informacji o tym, dlaczego jest to odpowiedź na pytanie.
Zainfekowany Drake
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.