CSS3 100vh non costante nel browser mobile


288

Ho un problema molto strano ... in ogni browser e versione mobile ho riscontrato questo comportamento:

  • tutti i browser hanno un menu principale quando si carica la pagina (mostrando ad esempio la barra degli indirizzi) che scorre verso l'alto quando si inizia a scorrere la pagina.
  • 100vh a volte viene calcolato solo sulla parte visibile di una finestra, quindi quando la barra del browser aumenta di 100vh (in termini di pixel)
  • tutto il layout rivernicia e regola nuovamente poiché le dimensioni sono cambiate
  • un brutto effetto nervoso per l'esperienza dell'utente

Come evitare questo problema? Quando ho sentito parlare di viewport-height ero eccitato e pensavo di poterlo usare per blocchi ad altezza fissa invece di usare javascript, ma ora penso che l'unico modo per farlo sia in realtà javascript con qualche evento di ridimensionamento ...

puoi vedere il problema su: sito di esempio

Qualcuno può aiutarmi con / suggerire una soluzione CSS?


semplice codice di prova:


1
se ho capito bene la domanda il problema che stai affrontando è nel browser mobile l'altezza è più che visibile viewport hight .. ok?
Gaurav Aggarwal,

Interessante, mai notato prima. È principalmente l'immagine di sfondo che è notevolmente instabile. Che ne dici di aggiungere un transition: 0.5so giù di lì, per rendere la modifica meno bruscamente?
C14L

@GauravAggarwal no, esattamente l'opposto: l'altezza reale del viewport è maggiore di quella fornita dal browser quando la sua barra degli indirizzi è visibile ...
Nereo Costacurta

1
Dato che la mia domanda sta diventando popolare, vorrei dare i miei 5 centesimi: non sarebbe più intelligente mantenere l'altezza reale della finestra e far scorrere solo la barra dei menu? non sembra così difficile. In effetti dovrebbe essere più facile ... dito su -> barra dei menu scorrere verso l'alto fino a quando invisibile, dito giù -> barra dei menu scorrere verso il basso fino a quando non è completamente visibile ... tutto insieme al corpo senza alcun effetto di regolazione e di salto ...
Nereo Costacurta,

4
Google ha alcune buone informazioni su questo: developers.google.com/web/updates/2016/12/url-bar-resizing Puoi usare il 100% anziché 100vh SE hai modificato l'altezza del corpo al 100%
Benisburgers

Risposte:


203

Purtroppo questo è intenzionale ...

Questo è un problema ben noto (almeno in Safari Mobile), che è intenzionale, in quanto previene altri problemi. Benjamin Poulain ha risposto a un bug del webkit :

Questo è completamente intenzionale. Ci è voluto un bel po 'di lavoro da parte nostra per ottenere questo effetto. :)

Il problema di base è questo: l'area visibile cambia in modo dinamico durante lo scorrimento. Se aggiorniamo l'altezza del viewport CSS di conseguenza, dobbiamo aggiornare il layout durante lo scorrimento. Non solo sembra una merda, ma farlo a 60 FPS è praticamente impossibile nella maggior parte delle pagine (60 FPS è il framerate di base su iOS).

È difficile mostrarti la parte "sembra merda", ma immagina mentre scorri, i contenuti si muovono e ciò che vuoi sullo schermo cambia continuamente.

L'aggiornamento dinamico dell'altezza non funzionava, avevamo alcune scelte: rilasciare le unità viewport su iOS, abbinare le dimensioni del documento come prima di iOS 8, utilizzare le dimensioni di visualizzazione piccole, utilizzare le dimensioni di visualizzazione grandi.

Dai dati che avevamo, l'utilizzo della dimensione della vista più grande era il miglior compromesso. La maggior parte dei siti Web che utilizzavano le unità di visualizzazione erano molto belli la maggior parte delle volte.

Nicolas Hoizey ha studiato un po 'questo: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

Nessuna correzione pianificata

A questo punto, non c'è molto da fare se non astenersi dall'usare l'altezza della finestra sui dispositivi mobili. Chrome è cambiato anche in questo nel 2016:


7
Sì, come penso. L'unica soluzione praticabile è una soluzione con script. Da quello che so, $(window).height()non è interessato da questo bug, quindi andrò in quel modo. grazie!
Nereo Costacurta,

3
Lo script è stato l'unico modo per me e ha funzionato perfettamente.
captDaylight,

461
La risposta più deprimente di sempre.
Winnemucca,

15
A partire dalla versione 56 di Chrome, la VH viene sempre calcolata come se la barra dell'URL fosse nascosta e quindi la VH non aumenta durante lo scorrimento, ad eccezione di position:fixed. Questo è simile all'implementazione su Safari. Ulteriori informazioni: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia,

4
L'ultimo Chrome non cambia vhné si <html> 100%alza dopo il caricamento iniziale. Questo è davvero fantastico - poiché il thrashing / reflow del layout quando la barra degli URL nasconde può sembrare spaventoso. Firefoxe Edge Mobileancora in modo dinamico aggiornare vhe <html>altezza, causando molti strappi e ridimensionamenti di tutti i componenti durante lo scorrimento, particolarmente male se si usano SVG per immagini di sfondo
Drenai,

143

Puoi provare min-height: -webkit-fill-available;nel tuo css invece di 100vh. Dovrebbe essere risolto


20
Vorrei lasciare min-height: 100vh;come fallback dove -webkit-fill-availablenon è supportato.
Tammy Tee,

Wow grazie!! Con questo non ho più bisogno di "reazioni-div-100vh"!
Andres Elizondo,

1
la risposta più utile!
Gauthier,

@TammyTee ha ragione, è meglio rimanere min-height: 100vh;lì perché altrimenti si romperà su browser come Firefox (almeno su desktop, iOS utilizza webkit indipendentemente dal browser).
JoniVR

2
Questa è stata una soluzione molto bella, ma sembra essere cambiata in iOS 13: vedo spazio extra nella parte inferiore della prima sezione, ma funziona esattamente come desiderato in Safari per iOS 12.x codepen.io/RwwL/pen / PowjvPq (dovresti forkare questo, quindi visualizzarlo in modalità debug CodePen su iOS per vedere davvero cosa intendo).
RwwL,

27

nella mia app lo faccio in questo modo (dattiloscritto e postcss nidificato, quindi modifica il codice di conseguenza):

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

nel tuo 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);
    }
}

funziona almeno su Chrome Mobile e iPad. Ciò che non funziona è quando aggiungi la tua app alla schermata Home su iOS e cambi l'orientamento alcune volte - in qualche modo i livelli di zoom si confondono con il valore innerHeight, potrei pubblicare un aggiornamento se trovo una soluzione.

dimostrazione


2
Questo è in realtà un approccio fantastico a un problema molto fastidioso.
Michael Giovanni Pumo,

1
Vorrei aggiungere un avvertimento che "@media not all e (hover: hover) {" non è un modo a prova di proiettile per rilevare i browser mobili. Sentiti libero di usare qualcos'altro.
Andreas Herd,

Mi piace molto questo approccio. Un commento che vorrei fare è sull'uso del listener di eventi. Ciò provoca una riverniciatura della pagina e la utilizzo solo in determinati scenari quando è assolutamente fondamentale per l'utente visualizzare la vista corretta (ovvero la vista iniziale della pagina iniziale)
Zach Gollwitzer

22

Per molti dei siti che costruisco il client chiederà un banner da 100VH e proprio come hai trovato, si traduce in una brutta esperienza "saltellante" sul cellulare quando inizi a scorrere. Ecco come risolvo il problema per un'esperienza uniforme e uniforme su tutti i dispositivi:

Ho prima impostato il mio elemento banner CSS su height:100vh

Quindi uso jQuery per ottenere l'altezza in pixel del mio elemento banner e applicare uno stile in linea usando questa altezza.

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

In questo modo si risolve il problema sui dispositivi mobili come quando la pagina viene caricata, l'elemento banner viene impostato su 100vh utilizzando CSS e quindi jQuery lo sostituisce inserendo CSS in linea nel mio elemento banner che ne impedisce il ridimensionamento quando un utente inizia a scorrere.

Tuttavia, sul desktop se un utente ridimensiona la finestra del browser, il mio elemento banner non verrà ridimensionato perché ora ha un'altezza fissa impostata in pixel a causa di jQuery sopra. Per risolvere questo problema, utilizzo Mobile Detect per aggiungere una classe "mobile" al corpo del mio documento. E poi avvolgo il jQuery sopra in un'istruzione if:

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

Di conseguenza, se un utente si trova su un dispositivo mobile, la classe "mobile" è presente sul corpo della mia pagina e viene eseguito il jQuery sopra. Quindi il mio elemento banner otterrà il CSS in linea applicato solo sui dispositivi mobili, mentre sul desktop rimane in vigore la regola CSS originale da 100 VH.


4
Vorrei modificarlo per usare $('.banner').css({ height: window.innerHeight });invece, che è l'altezza "reale" del viewport. Esempio di come funziona innerHeight
Martin

8
Che è document.querySelector('.banner').style.height = window.innerHeight;per le persone che scrivono JavaScript reale.
Fabian von Ellerts,

18

Guarda questa risposta: 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
soluzione intelligente, stavo affrontando un problema con l'altezza della finestra sui dispositivi mobili, anche questa dovrebbe essere accettata come soluzione (specialmente per i dispositivi mobili)
Julio Vedovatto

soluzione molto bella. Solo nel mio caso, non ho usato ponyfill, ma piuttosto la logica che per tutti i siti desktop uso 100vh, mentre per i dispositivi mobili utilizzo var (non abbiamo IE11 nel mondo mobile).
FrEaKmAn

L'unica soluzione che ha funzionato per gli elementi nidificati. In chiunque altro debba sostituire un paio di centinaia di istanze, puoi (almeno la maggior parte delle volte) abbinare regexp (-)? ([\ D.] +) Vh e sostituirlo con calc (var (- vh) * $ 1 $ 2)
ecc521 il

10

Mi è venuto in mente un componente React: verificalo se usi React o sfogli il codice sorgente se non lo fai, in modo da poterlo adattare al tuo ambiente.

Imposta l'altezza del div a schermo intero su window.innerHeighte quindi lo aggiorna al ridimensionamento della finestra.


3
Non tutti gli eroi indossano cappellini. Stai bene, per esempio. Grazie mille. risparmiato ore di tempo.
NarayaN,

E per gli amanti di Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan,

6

Dato che stavo cercando una soluzione qualche giorno, ecco la mia per tutti quelli che usano VueJS con Vuetify (la mia soluzione utilizza v-app-bar, v-navigation-drawer e v-footer): ho creato App.scss (utilizzato in App. vue) con il seguente contenuto:

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

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


1
Funziona bene e può essere generalizzato al di fuori del mondo Vue / Vuetify.
leo

5

Per me un simile trucco ha fatto un lavoro:

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

È diverso dall'altezza: 100%?
FF_Dev

Sì, 100vh è il 100% dell'altezza della finestra (il tuo dispositivo), quando il 100% puro riempie solo tutta l'area genitore disponibile (il blocco padre può avere ad esempio un'altezza di 50px e si riempirà solo a questa altezza padre). In breve.
Patryk Janik,

4

@nils lo ha spiegato chiaramente.

Qual è il prossimo, allora?

Sono appena tornato a utilizzare il relativo 'classico' %(percentuale) nei CSS.

Spesso è uno sforzo maggiore per implementare qualcosa di quanto non userebbe vh, ma almeno hai una soluzione abbastanza stabile che funziona su diversi dispositivi e browser senza strani problemi di interfaccia utente.


1
L'altezza: 100% per i banner salta ancora sui browser mobili. L'ho testato su Android e finora Chrome e Opera Mini forniscono il 100% dell'altezza VP visibile e non cambiano allo scorrimento. Edge e Firefox modificano l'altezza del 100% e ridipingono la vista. Firefox è particolarmente dannoso, con lacerazioni, rendering del testo, re-layout molto ovviamente
Drenai

1
@Drenai sei sicuro di implementarlo correttamente? Puoi dimostrarlo su un piccolo esempio su jsfiddle.net o giù di lì?
klimat,

Sì, sono sicuro :-)
Drenai,

3

Ho appena trovato un'app Web che ho progettato presenta questo problema con iPhone e iPad e ho trovato un articolo che suggerisce di risolverlo utilizzando query multimediali destinate a dispositivi Apple specifici.

Non so se posso condividere il codice di quell'articolo qui, ma l'indirizzo è questo: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Citando l'articolo: "abbina semplicemente l'altezza dell'elemento all'altezza del dispositivo usando le query multimediali che hanno come target le versioni precedenti di iPhone e iPad".

Hanno aggiunto solo 6 query multimediali per adattare gli elementi a tutta altezza e dovrebbe funzionare poiché è completamente implementato con CSS.

Modifica in sospeso: al momento non riesco a provarlo, ma tornerò e riferirò i miei risultati.


23
sto ancora aspettando quei risultati g
gman

2
Mi dispiace di non essere tornato come promesso. Ma dopo aver armeggiato con HTML e CSS per molto più tempo di quanto avrei voluto, ho cambiato il design del mio layout e ho trovato un modo che funzionava "ok" per le mie esigenze, ma non era una soluzione universale.
Jahaziel,

2

Dato che sono nuovo, non posso commentare altre risposte.

Se qualcuno è alla ricerca di una risposta per far funzionare questo (e può usare javascript - come sembra essere richiesto per farlo funzionare al momento) questo approccio ha funzionato abbastanza bene per me e tiene conto anche del cambiamento di orientamento mobile. Uso Jquery per il codice di esempio, ma dovrebbe essere fattibile con vanillaJS.

-Prima di tutto, utilizzo uno script per rilevare se il dispositivo è touch o hover. Esempio di ossa nude:

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

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

Ciò aggiunge classe all'elemento body in base al tipo di dispositivo (hover o touch) che può essere utilizzato in seguito per lo script height.

-Successivamente utilizzare questo codice per impostare l'altezza del dispositivo al caricamento e al cambio di orientamento:

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 è impostato in modo che il dispositivo calcoli correttamente la nuova altezza al cambio di orientamento. Se non c'è alcun timeout, nella mia esperienza l'altezza non sarà corretta. 500ms potrebbero essere un esagerato ma ha funzionato per me.

-100vh sui dispositivi hover è un fallback se il browser sovrascrive i CSS 100vh.


2
Touchstart non è supportato su tutti i browser e alcuni dispositivi non mobili dispongono di developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai

@Drenai Touchstart è supportato sulla maggior parte dei browser mobili. Sul desktop il supporto è un po 'meno, IE, Opera e Safari non lo supportano. Tuttavia, la domanda originale è rivolta a dispositivi mobili e non desktop. Il che rende questa una risposta valida.
Paolo,

Grazie per i commenti! Ho usato principalmente questa tattica per superare il riaggiustamento e il nervosismo dello schermo su alcuni browser mobili. Se il browser non supporta il touchstart, il fallback sarà il css 100vh - se 100vh non è supportato questo è un argomento diverso. Se il dispositivo desktop ha il supporto touch, l'altezza verrà calcolata con javascript. Perdiamo quindi il ricalcolo sul ridimensionamento fornito da 100vh, ma non è un problema fatale in generale poiché non ci sono cambiamenti di orientamento sul desktop.
tjgoodman,

Inoltre, il calcolo dell'altezza potrebbe anche essere collegato per ridimensionare l'evento, ma potremmo incorrere nello stesso esatto problema di riaggiustamento e jumpiness sui dispositivi mobili. Pertanto, ho trovato questa tattica pratica.
tjgoodman,

1
@Paul Se i browser desktop Chrome e Firefox hanno il touchstart, allora sicuramente è la funzione sbagliata da usare per rilevare un browser mobile? L'ho appena testato su Chrome e Firefox su Windows ed entrambi ho superato questo test
Drenai il

2

Il codice seguente ha risolto il problema (con jQuery).

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

E gli altri elementi usano %come unità da sostituire vh.


2

Ecco una soluzione che ho usato per la mia app React.

iPhone 11 Pro e iPhone Pro Max - 120px

iPhone 8 - 80px

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

È una soluzione di compromesso ma relativamente semplice


1

Per me ha funzionato:

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

L' bodyavrà l'altezza finestra visibile e #your-app-containercon height: 100%renderà tale contenitore prendere l'altezza finestra visibile.



0

Poiché non verrà risolto, puoi fare qualcosa del tipo:

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

# css
.content {
  height: 80vh;
}

Per me è stata la soluzione più veloce e pura del giocare con JavaScript che non poteva funzionare su molti dispositivi e browser.

Basta usare il giusto valore di vhquale si adatta alle tue esigenze.


0

L'uso di VH su dispositivi mobili non funzionerà con 100 VH, a causa delle loro scelte di progettazione che utilizzano l'intera altezza del dispositivo senza includere alcuna barra degli indirizzi, ecc.

Se stai cercando un layout che includa altezze div proporzionali alla vera altezza della vista, utilizzo la seguente soluzione css pura:

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

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

Sono disponibili due opzioni per impostare l'altezza della finestra, impostare manualmente --devHeight su un'altezza che funzioni (ma sarà necessario immettere questo valore per ogni tipo di dispositivo per cui si sta codificando)

o

Usa javascript per ottenere l'altezza della finestra e quindi aggiorna --devheight al caricamento e all'aggiornamento del viewport (tuttavia ciò richiede l'utilizzo di javascript e non è una soluzione CSS pura)

Una volta ottenuta la corretta altezza della vista, è possibile creare più div in una percentuale esatta dell'altezza totale della vista semplicemente modificando il moltiplicatore in ogni div a cui si assegna l'altezza.

0,10 = 10% dell'altezza della vista 0,57 = 57% dell'altezza della vista

Spero che questo possa aiutare qualcuno;)


1
Questa è una bella soluzione. Puoi leggere di più su questo in questo articolo su Css-tricks .
Amaury Hanser,

0

Puoi farlo aggiungendo il seguente script e stile

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

Stile

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

0

Prova html, body { height: 100% }qualcosa per l'effetto di 100VH sui dispositivi mobili.


-1

Puoi provare a dare position: fixed; top: 0; bottom: 0;proprietà al tuo contenitore.


2
Aggiungi maggiori informazioni sul perché questa è una risposta alla domanda.
Drake infetto il
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.