¿Previene el desplazamiento div hacia arriba cuando es golpeado por div debajo de él?


10

Ayer hice una pregunta similar, pero la expliqué mal y no especifiqué mi deseo de una solución de CSS puro, que creo que debería ser posible, así que lo estoy intentando nuevamente.

Básicamente, tengo un problema en el que tengo un div de mensajes desplazables y un campo de entrada debajo de él. Cuando hago clic en un botón, me gustaría que el campo de entrada se incremente 100 píxeles, sin que la división de mensajes se desplace también.

Aquí hay un violín que demuestra el problema en su totalidad

Como puede ver, cuando hace clic en el botón "Agregar margen", la división de mensajes también se desplaza hacia arriba. Me gustaría que se quedara donde estaba. Del mismo modo, si se desplaza ligeramente hacia arriba para que solo pueda ver el penúltimo mensaje, al hacer clic en el botón se debe mantener esa posición de manera similar al hacer clic.

Lo interesante es que este comportamiento se conserva "a veces". Por ejemplo, en algunas circunstancias (que no puedo deducir del todo) se conserva la posición de desplazamiento. Solo me gustaría que se comportara constantemente como tal.

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>

ingrese la descripción de la imagen aquí


1
Hola de nuevo, soy el que respondió su pregunta usando JS. Me gustaría informarle sobre algo que puede haber malinterpretado. Usted mencionó: los mensajes div también se desplazan hacia arriba . En realidad, no es lo divque se desplaza hacia arriba, es la altura del div que está disminuyendo. Entonces, ¿por qué es importante? Bueno, lo que significa es que scrollbarno está ajustando su posición. La barra de desplazamiento solo recuerda su posición relativa desde la parte superior de su contenedor. Cuando la altura del div disminuye, parece que la barra de desplazamiento se desplaza hacia arriba, pero en realidad no lo es.
Richard

1
Tu publicación tiene mucho sentido (en el sentido de que las personas saben qué resultado quieres), pero solo estoy compartiendo algo en caso de que el conocimiento no sea evidente para ti (en caso de que pueda ayudarte a tener una idea para resolver dicho problema) ;-)
Richard

Sí, muy cierto, gracias por la aclaración. Solo "parece" que se desplaza hacia arriba, porque no puede ver los contenidos más bajos que antes. En realidad, la posición del desplazamiento no está cambiando. Un poco de ilusión. Sí, eso podría ayudar a encontrar una solución.
Ryan Peschel

Respuestas:


5

Esta es una solución divertida que te puede gustar.

Lo que sabemos sobre el div es que conserva solo la posición superior de la barra de desplazamiento, por lo que si la altura cambia por algún motivo, la barra de desplazamiento seguirá siendo la misma y esto es lo que causa su problema.

Como solución alternativa, puede voltear .messages180 grados usando transform: rotate(180deg) scaleX(-1);y voltear hacia atrás .messagepara cancelar voltear el contenido, luego el div mantendrá la barra de desplazamiento inferior (que es superior) automáticamente.

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>


Esto tiene el problema de que el desplazamiento de la rueda del mouse está hacia atrás (el desplazamiento hacia arriba disminuye y el desplazamiento hacia abajo aumenta)
Ryan Peschel

Ok, pasé mucho tiempo en esto, pero aún ahora otros desarrolladores tienen 3 respuestas que no estás buscando :) Esperaba ayudarte a desearles buena suerte. pero como un voto positivo que pasa el tiempo tratando de responder a su pregunta, no se le cobrará por hacerlo.
Albahaca

2
Cosa segura. Gracias de todos modos
Ryan Peschel

Esto es muy inteligente ;-) Desafortunadamente, el desplazamiento inverso es un poco desagradable.
Richard

3

El comportamiento normal de la barra de desplazamiento es que esté en la parte superior, por lo que cuando lo configura en la parte inferior al cargar la página, debe mantenerlo usted mismo porque cuando el contenido a continuación lo empuja, entonces el desplazamiento div se moverá hacia la parte superior.

Entonces tengo dos soluciones para ti:

  1. Invierta los mensajes dentro del div de mensajes para que el último mensaje sea el primero y el desplazamiento siempre esté en la parte superior.

  2. Creé una función de JavaScript para desplazarse hasta la parte inferior de cualquier elemento, por lo que solo debe llamarla cuando quiera desplazarse hacia la parte inferior.

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

Revisa el fragmento

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>


Lamentablemente, no quiero desplazarme para desplazarme hasta la parte inferior. Quiero mantener su posición. Además, es probable que esto se pueda hacer sin Javascript. Desde el OP: "Del mismo modo, si se desplaza ligeramente hacia arriba para que solo pueda ver el penúltimo mensaje, hacer clic en el botón debería conservar esa posición de manera similar al hacer clic".
Ryan Peschel

3

Puede hacerlo al revés, dando la altura al en .messages lugar de dárselo al .containeren este caso, no afectará los mensajes, divpero si se lo da al .containerempujará su div porque el margen está dentro del principal div que tienen una altura.

Mira este fragmento

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>


Esto no funciona porque es necesario aplicar el margen .send-message. Si miras mi pregunta anterior, esto se hace porque es similar al aumento de margen de abrir el teclado virtual en dispositivos móviles (el margen inferior es solo un reemplazo 1: 1 para depurar la solución)
Ryan Peschel

3

La solución simple es establecer lo anterior scrollTopen alternar. Aquí estoy usando el conjunto de datos para almacenar el scrollTopvalor anterior , puede usar la variable tampoco.

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

Quiero decir, la siguiente es una solución única de CSS que resuelve exactamente su ejemplo:

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

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

pero no creo que te ayude con tu problema original del teclado virtual. Pero quizás esto pueda inspirar a otra persona a una solución.

El principal problema parece ser que la barra de desplazamiento ya está haciendo exactamente lo que desea:

Mantener su posición para que el contenido leído más recientemente sea visible.

Excepto que la barra de desplazamiento decide que desea ver la parte SUPERIOR del contenido visible actualmente, no la parte inferior. (Es más fácil ver si usa números: https://jsfiddle.net/1co3x48n/ )

Si está dispuesto a usar javascript, hay todo tipo de respuestas: https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com

Honestamente, el hecho de que pueda pasar por ~ 3 + páginas de esto y encontrar solo respuestas de Javascript me dice que no va a encontrar una respuesta solo de CSS, ciertamente no una que sea ampliamente compatible con el navegador.


0

Desafortunadamente, ninguna de las soluciones proporcionadas parece funcionar realmente. El problema con el uso onFocusde un cuadro de texto es que no se aplicará si el cuadro de texto ya tiene el foco. He estado jugando con esto durante horas y la solución más cercana que he encontrado hasta ahora es esta:

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

Sin embargo, esto solo parece funcionar a veces. Funciona el 100% del tiempo al hacer clic en el cuadro de texto, pero por alguna razón al cerrar el teclado virtual solo funciona si se desplaza lo suficiente hacia arriba. De lo contrario, se restablece a la misma posición cada vez (cerca del fondo, pero no del todo).

No estoy seguro de qué está causando eso. Tendré que investigar más mañana, supongo. Sin embargo, este es el más cercano hasta ahora.


Como todos dijimos en nuestras respuestas, parece imposible hacerlo solo con CSS. (Hay un problema con su código JS, que es que chatElem.scrollTop no puede ser inferior a 0 , por lo que si está demasiado cerca de la parte superior, intenta convertirse en un número negativo, se convierte en 0 en su lugar, y cuando hace que la pantalla sea más grande nuevamente, aparece un pergamino donde no había antes.)
Eliezer Berlin

Del mismo modo, chatElem.scrollTop no puede ser más que chatElem.scrollHeight - chatElem.offsetHeight , por lo que creará un desplazamiento adicional donde antes no había ninguno si intentas hacerlo.
Eliezer Berlin

0

Cree un contenedor como en los ejemplos anteriores, pero luego modifique el CSS cuando comience a escribir.

No configuro la posición de la barra de desplazamiento, solo muevo todo el contenedor de mensajes hacia arriba. Entonces, cualquiera que sea su posición de desplazamiento, permanecerá igual. Al menos hacia abajo, por supuesto, no podrá ver las líneas en la parte superior.

Debería poder ajustar el CSS a sus necesidades. Incluso podría ir sin JS si usa: foco o configuración CSS ligeramente diferente, sea 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

debe agregar document.querySelector(".messages").scrollTop = 10000;a la testfunción cuando agregue margen, luego scrollTopvaya al final en la carga que configuró scrolltopy cuando agregue margen o elimine el margen scrolltopno se configuró nuevamente, cambie su script para que le guste

<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

Parece que hay un problema con la función de alternancia js. Lo he cambiado a un simple escondite. También he agregado un ancho en la etiqueta de entrada. También debe agregar una posición: absoluta en el div de envío de mensaje y en la parte inferior: 0 para que permanezca en la parte inferior. luego coloque position: relative en el contenedor para que el mensaje de envío div permanezca dentro del contenedor. Esto significa que el contenedor de mensajes no afectará los mensajes en sí y los empujará hacia arriba.

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>


No entiendo esta solución. ¿Simplemente parece ocultar el cuadro de texto enviar mensaje cuando hace clic en agregar margen?
Ryan Peschel

-1

Un enfoque CSS imperfecto puro que se me ocurre es el siguiente:

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 sección superior tiene un pequeño problema cuando hay margen. El truco aquí es usar el margen negativo y usar la traducción para "desplazarse hacia arriba" (no realmente un desplazamiento, solo una ilusión) el wrapperdiv.


El tema de la sección superior es un poco problemático, sí. Además, ¿esto supone saber el tamaño exacto del teclado móvil para saber cuánto compensar? No estoy seguro de tener acceso a esa información.
Ryan Peschel

Para responder a tu pregunta, si. La técnica anterior debe conocer la altura exacta del teclado. Sin embargo, hay formas de usar JS para determinar la altura de un elemento y cambiar el CSS.
Richard

¿Cómo determinaría la altura del teclado virtual móvil?
Ryan Peschel

@RyanPeschel ¿Es ese teclado virtual un elemento de HTML (por ejemplo div)?
Richard

No estoy seguro de cómo está codificado.
Ryan Peschel

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.