Impedisci lo scorrimento del div in alto quando viene urtato dal div sotto di esso?


10

Ieri ho fatto una domanda simile ma l'ho spiegata male e non ho specificato il mio desiderio di una soluzione CSS puro, che penso dovrebbe essere possibile, quindi ci riprovo.

Fondamentalmente, ho un problema in cui ho un div di messaggi scorrevoli e un campo di input sotto di esso. Quando faccio clic su un pulsante, vorrei che il campo di input fosse ingrandito di 100 pixel, senza far scorrere anche il div dei messaggi.

Ecco un violino che dimostra il problema nella sua interezza

Come puoi vedere, quando fai clic sul pulsante "aggiungi margine", anche il div div scorre verso l'alto. Vorrei che restasse dov'era. Allo stesso modo, se si scorre leggermente verso l'alto in modo da poter vedere solo il penultimo messaggio, facendo clic sul pulsante si dovrebbe mantenere tale posizione allo stesso modo al clic.

La cosa interessante è che questo comportamento è "a volte" preservato. Ad esempio, in alcune circostanze (che non riesco a dedurre del tutto) la posizione di scorrimento viene mantenuta. Vorrei solo che si comportasse costantemente come tale.

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
};

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
     width: 400px;
     height: 300px;
     border: 1px solid #333;
     display: flex;
     flex-direction: column;
}
 .messages {
     overflow-y: auto;
     height: 100%;
}
 .send-message {
     width: 100%;
     display: flex;
     flex-direction: column;
}
 .some-margin {
     margin-bottom: 100px;
}
<div class="container">
   <div class="messages">
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
   </div>
   <div class="send-message">
      <input />
   </div>
</div>
<button onclick="test()">add margin</button>

inserisci qui la descrizione dell'immagine


1
Ciao di nuovo, sono io quello che ha risposto alla tua domanda usando JS. Vorrei informarti di una cosa che potresti aver frainteso. Hai menzionato: anche i messaggi div scorrono verso l'alto . In realtà, non è quello divche sta scorrendo verso l'alto, è l'altezza del div che sta diminuendo. Allora perché questo importa? Bene, ciò significa che scrollbarnon sta regolando la sua posizione. La barra di scorrimento ricorda solo la sua posizione relativa dalla parte superiore del suo contenitore. Quando l'altezza del div diminuisce, sembra che la barra di scorrimento scorra verso l'alto, ma in realtà non lo è.
Richard

1
Il tuo post ha perfettamente senso (nel senso che le persone sanno quale risultato vuoi), ma sto solo condividendo qualcosa nel caso in cui la conoscenza non ti sia chiara (nel caso possa aiutarti a trovare un'idea da risolvere detto problema) ;-)
Richard

Sì, è vero, grazie per il chiarimento. "Sembra" solo che sta scorrendo verso l'alto, perché non puoi vedere i contenuti più bassi che potresti una volta. In realtà la posizione di scorrimento non cambia. Un po 'di illusione. Sì, ciò potrebbe aiutare a trovare una soluzione.
Ryan Peschel,

Risposte:


5

Questa è una soluzione divertente che ti potrebbe piacere.

Quello che sappiamo del div che conserva solo la posizione superiore della barra di scorrimento, quindi se l'altezza è cambiata per qualsiasi motivo la barra di scorrimento rimarrà la stessa e questo è ciò che causa il problema.

Per ovviare al problema, puoi capovolgere di .messages180 gradi usando transform: rotate(180deg) scaleX(-1);e capovolgere indietro .messageper annullare il capovolgimento del contenuto, quindi il div manterrà automaticamente la barra di scorrimento inferiore (che è in alto).

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin")
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
  transform: rotate(180deg) scaleX(-1);
}

.message
{
  transform: rotate(180deg) scaleX(-1);
}
.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>


Questo ha il problema per cui lo scorrimento della rotellina del mouse è all'indietro (lo scorrimento verso l'alto scende e lo scorrimento verso il basso sale)
Ryan Peschel

Ok, ho trascorso molto tempo su questo, ma ancora ora altri sviluppatori hanno 3 risposte che non stai cercando :) Speravo di aiutarti a augurarti buona fortuna. ma a titolo di consiglio che trascorrono il tempo a cercare di rispondere alla tua domanda, non ti verrà addebitato alcun costo.
Basil

2
Cosa certa. Grazie comunque
Ryan Peschel il

Questo è molto intelligente ;-) Sfortunatamente, lo scrolling inverso è un po 'scoraggiante.
Richard

3

Il normale comportamento della barra di scorrimento è in alto, quindi quando lo si imposta in fondo al caricamento della pagina, è necessario mantenerlo autonomo perché ogni volta che il contenuto sottostante lo ha spinto, div scroll si sposta in alto.

Quindi ho due soluzioni per te:

  1. Invertire i messaggi all'interno dei messaggi div in modo che l'ultimo messaggio sia il primo, quindi lo scorrimento sarà sempre in alto.

  2. Ho creato una funzione javascript per scorrere fino alla fine di qualsiasi elemento in modo da chiamarla ogni volta che vuoi scorrere fino alla fine.

function scrollbottom(e)
    {
      e.scrollTop = e.clientHeight;
    }

Controlla lo snippet

var elem = document.querySelector(".messages");

window.onload = function(e){ 
  scrollbottom(elem);
}

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
  scrollbottom(elem);
}

function scrollbottom(e)
{
  e.scrollTop = e.clientHeight;
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>


Purtroppo non voglio scorrere solo per scorrere verso il basso. Voglio mantenere la sua posizione. Inoltre, è probabile che ciò possa essere fatto senza Javascript. Dall'OP: "Allo stesso modo, se si scorre leggermente verso l'alto in modo da poter vedere solo il penultimo messaggio, facendo clic sul pulsante si dovrebbe mantenere tale posizione allo stesso modo al clic".
Ryan Peschel,

3

Puoi farlo al contrario, dando l'altezza al .messages invece di darlo al.container in questo caso non influenzerà i messaggi divma se lo stai dando .containerspingerà il tuo div perché il margine è all'interno del principale div che hanno un'altezza.

Controlla questo frammento

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
  width: 400px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  height: 300px;
  overflow-y: auto;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 50px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>


Questo non funziona perché è necessario applicare il margine .send-message. Se guardi alla mia domanda precedente, questo è fatto perché è simile al bump del margine dall'apertura della tastiera virtuale sui dispositivi mobili (il margine inferiore è solo una sostituzione 1: 1 per il debug della soluzione)
Ryan Peschel

3

La semplice soluzione consiste nell'impostare il precedente scrollTopsu Attiva / Disattiva. Qui sto usando il set di dati per memorizzare il scrollTopvalore precedente , puoi usare anche la variabile.

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
}

function test() {
  let state = document.querySelector(".send-message").classList.toggle("some-margin")
  let div = document.querySelector(".messages");

  if (state) {
    div.dataset.top = div.scrollTop;
    div.scrollTop += 100 // same as margin-bottom of .some-margin
  } else {
    div.scrollTop = div.dataset.top;
  }
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
    <div class="message">hello1</div>
    <div class="message">hello2</div>
    <div class="message">hello3</div>
    <div class="message">hello4</div>
    <div class="message">hello5</div>
    <div class="message">hello6</div>
    <div class="message">hello7</div>
    <div class="message">hello8</div>
    <div class="message">hello9</div>
    <div class="message">hello10</div>
    <div class="message">hello11</div>
    <div class="message">hello12</div>
    <div class="message">hello13</div>
    <div class="message">hello14</div>
    <div class="message">hello15</div>
    <div class="message">hello16</div>
    <div class="message">hello17</div>
    <div class="message">hello18</div>
    <div class="message">hello19</div>
    <div class="message">hello20</div>
  </div>
  <div class="send-message">
    <input />
  </div>
</div>

<button onclick="test()">add margin</button>


0

Voglio dire, la seguente è una soluzione solo CSS che risolve esattamente il tuo esempio:

.some-margin{margin-bottom:100px;}

.messages{margin-top:-100px;}

ma non credo che ti aiuterà con il tuo problema originale con la tastiera virtuale. Ma forse questo può ispirare qualcun altro a una soluzione.

Il problema principale sembra essere che la barra di scorrimento sta già facendo esattamente quello che vuoi:

Mantenere la sua posizione in modo che sia visibile il contenuto letto più di recente.

Tranne che la barra di scorrimento decide che si desidera visualizzare la parte superiore del contenuto attualmente visibile, non la parte inferiore. (È più facile vedere se usi i numeri: https://jsfiddle.net/1co3x48n/ )

Se sei disposto a utilizzare JavaScript, ci sono tutti i tipi di risposte: https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com

Onestamente, il fatto che tu possa passare attraverso ~ 3 + pagine di questo e trovare solo risposte Javascript mi ​​dice che non troverai una risposta solo CSS, certamente non una che sia ampiamente compatibile con il browser.


0

Purtroppo nessuna delle soluzioni fornite sembra funzionare davvero. Il problema con l'utilizzo onFocusdi una casella di testo è che non si applica se la casella di testo ha già lo stato attivo. Sto scherzando con questo da ore e la soluzione più vicina che ho trovato finora è questa:

componentDidMount() {
  this.screenHeight = window.innerHeight;

  let chatElem = document.querySelector(".conversations-chat");

  window.addEventListener('resize', () => {
    let diff = this.screenHeight - window.innerHeight;

    chatElem.scrollTop += diff;

    this.screenHeight = window.innerHeight;      
  });
}

Tuttavia, questo a volte sembra funzionare. Funziona al 100% delle volte quando si fa clic sulla casella di testo, ma per qualche motivo quando si chiude la tastiera virtuale funziona solo se è stato fatto scorrere verso l'alto abbastanza. Altrimenti si reimposta ogni volta nella stessa posizione (vicino al fondo, ma non proprio in fondo).

Non sono sicuro di cosa lo stia causando. Dovrò indagare di più domani suppongo. Questo è il più vicino finora.


Come abbiamo detto tutti nelle nostre risposte, sembra impossibile avere a che fare solo con i CSS. (C'è un problema con il tuo codice JS, che è che chatElem.scrollTop non può essere inferiore a 0 , quindi se è troppo vicino all'inizio, cerca di diventare un numero negativo, diventa invece 0, quindi quando ingrandisci lo schermo di nuovo, appare una pergamena dove prima non c'era.)
Eliezer Berlin

Allo stesso modo, chatElem.scrollTop non può essere altro che chatElem.scrollHeight - chatElem.offsetHeight , quindi creerà uno scorrimento extra dove non ce n'era prima se provi a farlo.
Eliezer Berlin,

0

Crea un contenitore come negli esempi precedenti, ma modifica semplicemente il CSS quando inizi a digitare.

Non imposto la posizione della barra di scorrimento, basta spostare l'intero contenitore dei messaggi verso l'alto. Quindi qualunque sia la tua posizione di scorrimento, rimarrà la stessa. Almeno verso il basso, ovviamente non sarai in grado di vedere le linee in alto.

Dovresti essere in grado di modificare il CSS in base alle tue esigenze. Potresti anche andare senza JS se usi: focus o configurazione CSS leggermente diversa, sii creativo :)

function activate(){
    document.querySelector("#container").classList.toggle("active");
  }
#container{
    position:relative;
    width:300px;
    height:100vh;
    max-height:230px;
    overflow:hidden;
  }
  #messages{
    position:absolute;
    bottom:30px;
    overflow:auto;
    height:200px;
    width:100%;
    background:#eee;
  }
  #container.active #messages {
    bottom:100px;
  }
  #send-message{
    position:absolute;
    border:1px solid #ccc;
    bottom:0;
    height:28px;
  }
  #container.active #send-message{
    height:100px;
  }
<div id="container">
  <div id="messages">
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  </div>
  <div id="send-message">
    <input id="newmessage" onclick="activate()" type="text" />
  </div>
</div>


0

devi aggiungere document.querySelector(".messages").scrollTop = 10000;alla testfunzione quando aggiungi margine e poi scrollTopvai a termine con il carico che hai impostato scrolltope quando aggiungi margine o rimuovi margine scrolltopnon impostato di nuovo cambia lo script in questo modo

<script>
    window.onload = function(e){ 
    document.querySelector(".messages").scrollTop = 10000;
}

function test() {
    document.querySelector(".send-message").classList.toggle("some-margin")
            document.querySelector(".messages").scrollTop = 10000;
}
    </script>

0

Sembra esserci un problema con la funzione di attivazione / disattivazione js. L'ho cambiato in un semplice nascondino. Inoltre ho aggiunto una larghezza al tag di input. Devi anche aggiungere una posizione: assoluta sul div del messaggio di invio e in basso: 0 in modo che rimanga in fondo. quindi posizionare position: relative sul contenitore in modo che il div del messaggio di invio rimanga all'interno del contenitore. Ciò significa che il contenitore dei messaggi non influirà sui messaggi stessi e li spingerà verso l'alto.

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
};

function test() {
  var x = document.querySelector(".send-message");

  if (x.style.display === "none") {
    x.style.display = "block";
  } else {
    x.style.display = "none";
  }
}
.container {
     width: 400px;
     height: 300px;
     border: 1px solid #333;
     display: flex;
     flex-direction: column;
     position: relative;
}
 .messages {
     overflow-y: auto;
     height: 100%;
}
 .send-message {
     width: 100%;
     display: flex;
     flex-direction: column;
     position: absolute;
     bottom: 0;
}
 .send-message input {
     width:100%;
}
 .some-margin {
     margin-bottom: 100px;
}

 
 
<div class="container">
   <div class="messages">
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">test</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
   </div>
   <div class="send-message">
      <input />
   </div>
</div>
<button onclick="test()">add margin</button>


Non capisco questa soluzione. Sembra solo nascondere la casella di testo Invia messaggio quando fai clic su Aggiungi margine?
Ryan Peschel,

-1

Un puro approccio CSS imperfetto che posso trovare è il seguente:

window.onload = function(e){ 
  document.querySelector(".messages").scrollTop = 10000;
}

document.querySelector('button').addEventListener('click', test)

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin")
  document.querySelector('.wrapper').classList.toggle('wrapper--transformed')
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  position: relative;
  overflow-y: auto;
  height: 100%;
}

.wrapper {
  display: block;
}

.wrapper--transformed {
  transform: translateY(-100px);
  margin-bottom: -100px;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
    <div class = "wrapper">
      <div class="message">hello1</div>
      <div class="message">hello2</div>
      <div class="message">hello3</div>
      <div class="message">hello4</div>
      <div class="message">hello5</div>
      <div class="message">hello6</div>
      <div class="message">hello7</div>
      <div class="message">hello8</div>
      <div class="message">hello9</div>
      <div class="message">hello1</div>
      <div class="message">hello2</div>
      <div class="message">hello3</div>
      <div class="message">hello4</div>
      <div class="message">hello5</div>
      <div class="message">hello6</div>
      <div class="message">hello7</div>
      <div class="message">hello8</div>
      <div class="message">hello9</div>
      <div class="message">hello1</div>
      <div class="message">hello2</div>
    </div>
  </div>
  <div class="send-message">
    <input />
  </div>
</div>
<button>add margin</button>

La sezione più in alto ha un piccolo problema quando c'è margine. Il trucco qui è usare il margine negativo e usare la traduzione per "scorrere verso l'alto" (non proprio una pergamena, solo un'illusione) il wrapperdiv.


Il problema della sezione più in alto è un po 'un problema, sì. Inoltre, si presume che conosca le dimensioni esatte della tastiera del cellulare per sapere quanto compensare? Non sono sicuro di avere accesso a tali informazioni.
Ryan Peschel,

Per rispondere alla tua domanda, sì. La tecnica sopra descritta deve conoscere l'altezza esatta della tastiera. Tuttavia, ci sono modi usando JS per determinare l'altezza di un elemento e modificare il CSS.
Richard

Come determineresti l'altezza della tastiera virtuale mobile?
Ryan Peschel,

@RyanPeschel Quella tastiera virtuale è un elemento di HTML (ad es. div)?
Richard

Non sono sicuro di come sia codificato.
Ryan Peschel,

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.