Empêcher div de défiler vers le haut lorsqu'il est heurté par div en dessous?

10

J'ai posé une question similaire hier, mais je l'ai mal expliqué et je n'ai pas précisé mon désir d'une solution pure CSS, ce qui, je pense, devrait être possible, alors j'essaie à nouveau.

Fondamentalement, j'ai un problème où j'ai une div de messages déroulants et un champ de saisie en dessous. Lorsque je clique sur un bouton, je souhaite que le champ de saisie soit augmenté de 100 pixels, sans que la division des messages défile également.

Voici un violon qui illustre le problème dans son intégralité

Comme vous pouvez le voir, lorsque vous cliquez sur le bouton "ajouter une marge", la div des messages défile également vers le haut. J'aimerais qu'il reste où il était. De même, si vous faites légèrement défiler vers le haut de sorte que vous ne puissiez voir que l'avant-dernier message, un clic sur le bouton devrait également conserver cette position lors du clic.

La chose intéressante est que ce comportement est "parfois" préservé. Par exemple, dans certaines circonstances (que je ne peux pas vraiment déduire), la position de défilement est conservée. Je voudrais juste qu'il se comporte toujours comme tel.

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>

entrez la description de l'image ici

Ryan Peschel
la source
1
Bonjour à nouveau, c'est moi qui ai répondu à votre question en utilisant JS. Je voudrais vous informer d'une chose que vous avez peut-être mal interprétée. Vous avez mentionné: la div des messages défile également vers le haut . En fait, ce n'est pas le divqui défile, c'est la hauteur du div qui diminue. Alors, pourquoi est-ce important? Eh bien, cela signifie que l' scrollbarajustement de sa position n'est pas. La barre de défilement ne se souvient que de sa position par rapport au haut de son conteneur. Lorsque la hauteur du div diminue, il semble que la barre de défilement défile vers le haut, mais ce n'est pas le cas.
Richard
1
Votre message est parfaitement logique (dans le sens où les gens savent quel résultat vous voulez), mais je partage juste quelque chose au cas où la connaissance ne vous serait pas apparente (au cas où cela pourrait vous aider à trouver une idée à résoudre dit problème) ;-)
Richard
Ouais très vrai, merci pour la clarification. Il semble seulement qu'il défile vers le haut, car vous ne pouvez pas voir le contenu inférieur que vous pouviez une fois. En réalité, la position du défilement ne change pas. Un peu d'illusion. Oui, cela pourrait aider à trouver une solution.
Ryan Peschel

Réponses:

5

C'est une solution amusante qui pourrait vous plaire.

Ce que nous savons sur le div, c'est qu'il ne conserve que la position supérieure de la barre de défilement, donc si la hauteur a changé pour une raison quelconque, la barre de défilement restera la même et c'est ce qui cause votre problème.

Comme solution de contournement, vous pouvez inverser le .messages180 degrés en utilisant transform: rotate(180deg) scaleX(-1);et inverser le .messagepour annuler le retournement du contenu, puis la div maintiendra automatiquement la barre de défilement inférieure (qui est en haut).

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>

Basilic
la source
Cela a le problème où le défilement de la molette de la souris est en arrière (le défilement vers le haut descend et le défilement vers le bas monte)
Ryan Peschel
Ok, j'ai passé beaucoup de temps là-dessus mais encore maintenant, d'autres développeurs ont 3 réponses que vous ne cherchez pas :) J'espérais vous aider à vous souhaiter bonne chance. mais en tant que conseil avisé qui passent du temps à essayer de répondre à votre question, vous ne serez pas facturé en le faisant.
Basil
2
Chose sûre. Merci quand même
Ryan Peschel
C'est très intelligent ;-) Malheureusement, le défilement inversé est un peu rebutant.
Richard
3

Le comportement normal de la barre de défilement doit être en haut, donc lorsque vous le placez au bas lors du chargement de la page, vous devez le maintenir vous-même car chaque fois que le contenu ci-dessous le poussait, le défilement div se déplacera vers le haut.

J'ai donc deux solutions pour vous:

  1. Inversez les messages à l'intérieur de la div des messages pour que le dernier message soit le premier afin que le défilement soit toujours en haut.

  2. J'ai créé une fonction javascript pour faire défiler vers le bas de n'importe quel élément afin que vous l'appeliez quand vous voulez faire défiler vers le bas.

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

Vérifier l'extrait

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>

Basilic
la source
Malheureusement, je ne veux pas faire défiler pour simplement défiler vers le bas. Je veux maintenir sa position. En outre, il est probable que cela puisse se faire sans Javascript. À partir de l'OP: "De même, si vous faites légèrement défiler vers le haut de sorte que vous ne pouvez voir que l'avant-dernier message, un clic sur le bouton devrait également conserver cette position lors du clic."
Ryan Peschel
3

Vous pouvez le faire dans l'autre sens, en donnant la hauteur au au .messages lieu de le donner au .containerdans ce cas cela n'affectera pas les messages divmais si vous le donnez au .containeril poussera votre div car la marge est à l'intérieur du principal div qui ont une hauteur.

Vérifiez cet extrait

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>

Basilic
la source
Cela ne fonctionne pas car la marge doit être appliquée .send-message. Si vous regardez ma question précédente, cela est fait car cela s'apparente à la bosse de marge de l'ouverture du clavier virtuel sur les appareils mobiles (le bas de la marge est juste un remplacement 1: 1 pour déboguer la solution)
Ryan Peschel
3

La solution de contournement simple consiste à scrollTopactiver / désactiver la précédente . Ici, j'utilise un ensemble de données pour stocker la scrollTopvaleur précédente , vous pouvez utiliser une variable non plus.

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>

User863
la source
0

Je veux dire, ce qui suit est une solution CSS uniquement qui résout exactement votre exemple:

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

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

mais je ne pense pas que cela vous aidera avec votre problème d'origine du clavier virtuel. Mais cela peut peut-être inspirer quelqu'un d'autre à trouver une solution.

Le principal problème semble être que la barre de défilement fait déjà exactement ce que vous voulez:

Maintenir sa position pour que le contenu le plus récemment lu soit visible.

Sauf que la barre de défilement décide que vous vouliez voir le HAUT du contenu actuellement visible, pas le bas. (Il est plus facile de voir si vous utilisez des nombres: https://jsfiddle.net/1co3x48n/ )

Si vous souhaitez utiliser javascript, il existe toutes sortes de réponses: https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com

Honnêtement, le fait que vous puissiez parcourir ~ 3 + pages de cela et ne trouver que des réponses Javascript me dit que vous n'allez pas trouver une réponse uniquement CSS, certainement pas une réponse largement compatible avec le navigateur.

Eliezer Berlin
la source
0

Malheureusement, aucune des solutions proposées ne semble fonctionner. Le problème avec l'utilisation onFocusd'une zone de texte est qu'elle ne s'applique pas si la zone de texte a déjà le focus. Je joue avec ça depuis des heures et la solution la plus proche que j'ai trouvée jusqu'à présent est la suivante:

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

Cependant, cela ne semble que parfois fonctionner. Cela fonctionne 100% du temps lorsque vous cliquez sur la zone de texte, mais pour une raison quelconque, lorsque vous fermez le clavier virtuel, cela ne fonctionne que s'il a été suffisamment défilé. Sinon, il revient à la même position à chaque fois (près du bas, mais pas tout à fait en bas).

Je ne sais pas ce qui cause ça. Devra enquêter davantage demain je suppose. C'est le plus proche jusqu'ici cependant.

Ryan Peschel
la source
Comme nous l'avons tous dit dans nos réponses, cela semble impossible à faire avec seulement CSS seul. (Il y a un problème avec votre code JS, qui est que chatElem.scrollTop ne peut pas être inférieur à 0 , donc s'il est trop près du haut, il essaie de devenir un nombre négatif, devient 0 à la place, puis lorsque vous agrandissez l'écran encore une fois, un parchemin apparaît là où il n'y en avait pas auparavant.)
Eliezer Berlin
De la même façon, chatElem.scrollTop ne peut pas être plus que chatElem.scrollHeight - chatElem.offsetHeight , donc cela créera un défilement supplémentaire là où il n'y en avait pas auparavant si vous essayez de le faire.
Eliezer Berlin
0

Créez un conteneur comme dans les exemples ci-dessus, mais modifiez simplement le CSS lorsque vous commencez à taper.

Je ne définit pas la position de la barre de défilement, déplacez simplement le conteneur de messages vers le haut. Quelle que soit votre position de défilement, elle restera la même. Au moins vers le bas, bien sûr, vous ne pourrez pas voir les lignes en haut.

Vous devriez pouvoir ajuster le CSS à vos besoins. Vous pourriez même vous passer de JS si vous utilisez: focus ou configuration CSS légèrement différente, soyez créatif :)

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>

Rmaxx
la source
0

vous devez ajouter document.querySelector(".messages").scrollTop = 10000;à la testfonction lorsque vous ajoutez de la marge, puis scrollToppassez à la fin de la charge que vous avez définie scrolltopet lorsque vous ajoutez ou supprimez une marge scrolltopn'a pas défini à nouveau, changez votre script pour l'aimer

<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>
MBadrian
la source
0

Il semble y avoir un problème avec la fonction bascule js. Je l'ai changé en un simple cache-cache. J'ai également ajouté une largeur sur la balise d'entrée. Vous devez également ajouter une position: absolue sur la div du message d'envoi et en bas: 0 pour qu'elle reste en bas. puis placez position: relative sur le conteneur pour que le message d'envoi div reste dans le conteneur. Cela signifie que le conteneur de messages n'affectera pas les messages eux-mêmes et ne les poussera pas vers le haut.

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>

Shannaki
la source
Je ne comprends pas cette solution. Il semble juste masquer la zone de texte d'envoi du message lorsque vous cliquez sur ajouter une marge?
Ryan Peschel
-1

Une approche CSS imparfaite pure que je peux trouver est la suivante:

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 section supérieure a un petit problème lorsque la marge est là. L'astuce consiste à utiliser une marge négative et à utiliser translate pour «faire défiler vers le haut» (pas vraiment un parchemin, juste une illusion) le wrapperdiv.

Richard
la source
Le problème de la section la plus élevée est un peu un problème, oui. En outre, cela suppose-t-il de connaître la taille exacte du clavier mobile pour savoir combien compenser? Je ne sais pas si j'ai accès à ces informations.
Ryan Peschel
Pour répondre à ta question, oui. La technique ci-dessus doit connaître la hauteur exacte du clavier. Cependant, il existe des moyens d'utiliser JS pour déterminer la hauteur d'un élément et modifier le CSS.
Richard
Comment détermineriez-vous la hauteur du clavier virtuel mobile?
Ryan Peschel
@RyanPeschel Ce clavier virtuel est-il un élément HTML (par exemple div)?
Richard
Je ne sais pas comment c'est codé.
Ryan Peschel
-2

Il peut très simplement être adressé avec des noms d'ancrage. Regardez le violon JS sur https://jsfiddle.net/gvbz93sx/

Modification HTML <a name="bottom"></a>

Changement JS document.location.hash = "#bottom";

Animesh
la source