programing

바깥쪽 div 크기가 변경되면 스크롤 가능한 div를 바닥에 고정합니다.

lastcode 2023. 3. 23. 22:46
반응형

바깥쪽 div 크기가 변경되면 스크롤 가능한 div를 바닥에 고정합니다.

다음은 채팅 앱 예시입니다.->

는, 「아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,.messages-container을 사용법★★★내.messages-container,.scroll는 메시지 목록을 보관하고 있습니다.메시지가 화면 크기보다 많을 경우 스크롤합니다.

이제 다음 경우를 생각해 보겠습니다.

  1. 사용자가 대화의 맨 아래까지 스크롤합니다.
  2. .text-input으로

사용자가 컨버세이션의 맨 아래까지 스크롤하지 않고 텍스트 입력이 증가하여 맨 아래가 표시되지 않게 됩니다.

리액트를 사용하는 경우 텍스트 입력 높이를 계산하고 변경 사항이 있을 경우 .messages-container에 알립니다.

componentDidUpdate() {
  window.setTimeout(_ => {
    const newHeight = this.calcHeight();
    if (newHeight !== this._oldHeight) {
      this.props.onResize();
    }
    this._oldHeight = newHeight;
  });
}

하지만 이는 눈에 보이는 성능 문제를 야기하고, 이렇게 메시지를 전달하는 것은 슬픈 일입니다.

더은 방법 ?? ???를 사용하여 .text-input-input-input-increments가 증가할 때 기본적으로 .text-input-input-increments를 할 수 ?shift up.

2: 이 답변의 두 번째 수정

는 기기입니다.flex-direction: column-reverse;예를 들어 Skype나 다른 많은 채팅 앱처럼 메시지 컨테이너의 맨 아래에 메시지를 정렬하면서 모든 것을 묻습니다.

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }

「 」의 flex-direction: column-reverse;는 IE/Edge/Firefox의 버그입니다.스크롤바는 표시되지 않습니다.자세한 내용은 다음 URL에서 확인할 수 있습니다.Firefox/IE의 Flexbox column-reverseoverflow 입니다.

장점은 모바일/태블릿에서 최대 90%, 데스크톱에서 최대 65%의 브라우저를 지원한다는 것입니다.또한 버그가 수정되면 회피책이 있습니다.

// scroll to bottom
function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}

코드 두 flex-direction: column-reverse;

function addContent () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.value.length > 0) {
    msgdiv.innerHTML += msgtxt.value + '<br/>';
    msgtxt.value = "";
  } else {
    msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}

function resizeInput () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.style.height == '120px') {
    msgtxt.style.height = 'auto';
  } else {
    msgtxt.style.height = '120px';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}


/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;

function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }


/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }

/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
  .chat-messages-text{ overflow: visible; }
  /*  reset Edge as it identifies itself as webkit  */
  @supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
@-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
  <div class="chat-messages">
    <div class="chat-messages-text" id="messages">
      Long long content 1!<br/>
      Long long content 2!<br/>
      Long long content 3!<br/>
      Long long content 4!<br/>
      Long long content 5!<br/>
    </div>
  </div>
  <div class="chat-input">
    <textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
    <button onclick="addContent();">Add msg</button>
    <button onclick="resizeInput();">Resize input</button>
  </div>
</div>


Side Note 1: 이 검출 방법은 완전히 테스트되지 않았지만 새로운 브라우저에서 작동합니다.

사이드 노트 2: 채팅 입력에 크기 조정 이벤트핸들러를 접속하는 것이 update Scroll 함수를 호출하는 것보다 효율적일 수 있습니다.

주의: HaZardouS의 html 구조 재사용 크레딧

필요한 CSS 규칙 세트는 1개뿐입니다.

.messages-container, .scroll {transform: scale(1,-1);}

됐어, 넌 끝났어!

구조:먼저 컨테이너 요소를 수직으로 플립하여 맨 위가 맨 아래가 되도록 한 다음(원하는 스크롤 방향을 제공), 메시지가 뒤집히지 않도록 내용 요소를 플립합니다.

이 접근 방식은 최신 브라우저 모두에서 작동합니다.다만, 이상한 부작용이 있습니다.메시지 상자에서 마우스 휠을 사용하면 스크롤 방향이 반대입니다.이것은 아래와 같이 JavaScript의 몇 줄로 수정할 수 있습니다.

여기 데모와 바이올린이 있습니다.

//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
  if(e.deltaY) {
    e.preventDefault();
    e.currentTarget.scrollTop -= e.deltaY;
  }
});

//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
  var inp = document.querySelector('.text-input');
  document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
  inp.value = '';
  inp.focus();
}
resize = function() {
  var inp = document.querySelector('.text-input');
  inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.messages-container {
  flex-shrink: 10;
  height: 100%;
  overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
  <div class="messages-container">
    <div class="scroll">
      <p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
      <p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10<p>Message 11<p>Message 12<p>Message 13<p>Message 14<p>Message 15<p>Message 16<p>Message 17<p>Message 18<p>Message 19<p>Message 20
    </div>
  </div>
  <textarea class="text-input" autofocus>Your message</textarea>
  <div>
    <button id="send" onclick="send();">Send input</button>
    <button id="resize" onclick="resize();">Resize input box</button>
  </div>
</div>

편집: 스크롤 코드의 심플화를 제안해 주신 @Some Special 덕분입니다!

다음 바이올린을 사용해 보세요.https://jsfiddle.net/Hazardous/bypxg25c/현재 바이올린은 jQuery를 사용하여 텍스트 영역을 확대/크기 조정하고 있지만, 핵심은 메시지 컨테이너 및 입력 컨테이너 클래스에 사용되는 플렉스 관련 스타일입니다.

.messages-container{
  order:1;
  flex:0.9 1 auto;
  overflow-y:auto;
  display:flex;
  flex-direction:row;
  flex-wrap:nowrap;
  justify-content:flex-start;
  align-items:stretch;
  align-content:stretch;
}

.input-container{
  order:2;
  flex:0.1 0 auto;
}

flex-shrink 값은 .messages-container의 경우 1, .input-container의 경우 0으로 설정됩니다.이렇게 하면 크기가 재할당될 때 메시지컨테이너가 축소됩니다.

text-input의 범위 내에서messages의 맨 하고 absolute를 지정하면 absolute는 "Absolute"로 지정합니다.messages충분한 하단 패딩 공간을 확보해야 합니다.

가지 를 실행하여 를 에 합니다.conversation가 바뀌게 됩니다.text-input 「」의 messagesCSS를 사용하다

JavaScript는 CSS 이행 실행과 동시에 "scrollTo" 함수를 실행하여 스크롤을 맨 아래에 유지합니다.

맨 때, 뺄 수 있습니다.conversation

이게 도움이 됐으면 좋겠다.

https://jsfiddle.net/cnvzLfso/5/

var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');

function scrollTo(element, to, duration) {
  if (duration <= 0) {
    doScollCheck = true;
    return;
  }
  var difference = to - element.scrollTop;
  var perTick = difference / duration * 10;

  setTimeout(function() {
    element.scrollTop = element.scrollTop + perTick;
    if (element.scrollTop === to) {
      doScollCheck = true;
      return;
    }
    scrollTo(element, to, duration - 10);
  }, 10);
}

function resizeInput(atBottom) {
  var className = 'bigger',
    hasClass;
  if (objConv.classList) {
    hasClass = objConv.classList.contains(className);
  } else {
    hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
  }
  if (atBottom) {
    if (!hasClass) {
      doScollCheck = false;
      if (objConv.classList) {
        objConv.classList.add(className);
      } else {
        objConv.className += ' ' + className;
      }
      scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
    }
  } else {
    if (hasClass) {
      if (objConv.classList) {
        objConv.classList.remove(className);
      } else {
        objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
      }
    }
  }
}

objMessages.addEventListener('scroll', function() {
  if (doScollCheck) {
    var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
    resizeInput(isBottom);
  }
});
html,
body {
  height: 100%;
  width: 100%;
  background: white;
}
body {
  margin: 0;
  padding: 0;
}
.conversation {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  position: relative;
}
.messages {
  overflow-y: scroll;
  padding: 10px 10px 60px 10px;
  -webkit-transition: padding .5s;
  -moz-transition: padding .5s;
  transition: padding .5s;
}
.text-input {
  padding: 10px;
  -webkit-transition: height .5s;
  -moz-transition: height .5s;
  transition: height .5s;
  position: absolute;
  bottom: 0;
  height: 50px;
  background: white;
}
.conversation.bigger .messages {
  padding-bottom: 110px;
}
.conversation.bigger .text-input {
  height: 100px;
}
.text-input input {
  height: 100%;
}
<div class="conversation">
  <div class="messages">
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is the last message
    </p>
    <div class="text-input">
      <input type="text" />
    </div>
  </div>
</div>

당신은 글을 쓴다;

Now, consider this case:

    The user scrolls to the bottom of the conversation
    The .text-input, dynamically gets bigger

.text-input을 동적으로 설정하는 방법은 this.props.onResize()를 실행하는 논리적인 장소가 되지 않을까요?

관련되시는 분께,

위의 답변으로는 내 질문이 충분하지 않았다.

내가 찾은 해결책은 내 이너폭과 이너폭을 만드는 것이다.높이 변수 상수 - 스크롤 막대에 적응하기 위해 스크롤 시 브라우저의 innerWidth가 변경됩니다.

var innerWidth = window.innerWidth
var innerHeight = window.innerHeight

OR FOR REACT

this.setState({width: window.innerWidth, height: window.innerHeight})

즉, 이를 무시하려면 스크롤하지 않는 것처럼 모든 것을 일정하게 만들어야 합니다.크기 조정/방향 변경 시 업데이트해야 합니다!

오스카

IMHO의 현재 답변은 정답이 아닙니다: 1/flex-direction: column-reverse; 메시지 순서를 거꾸로 합니다.원하지 않았습니다.2/ javascript도 약간 해킹적이고 쓸모없는 것이 있습니다.

속성을 가진 스페이서 상자를 사용하여 PRO와 동일하게 만들려면 다음 절차를 따릅니다.

flex-grow: 1;
flex-basis: 0;

는 메시지 위에 있습니다.채팅 입력으로 이동합니다.사용자가 새 메시지를 입력하고 입력 높이가 증가하면 스크롤 막대가 위로 이동하지만 메시지가 전송되면(입력 클리어), 스크롤 막대는 다시 맨 아래로 돌아갑니다.

내 스니펫을 확인합니다.

body {
    background: #ccc;
}
.chat {
  display: flex;
  flex-direction: column;
  width: 300px;
  max-height: 300px;
  max-width: 90%;
  background: #fff;
}
.spacer-box {
    flex-basis: 0;
    flex-grow: 1;
  }
  .messages {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    flex-grow: 1;
    padding: 24px 24px 4px;
  }
  .footer {
    padding: 4px 24px 24px;
  }
  #chat-input {
      width: 100%;
      max-height: 100px;
      overflow-y: auto;
      border: 1px solid pink;
      outline: none;
      user-select: text;
      white-space: pre-wrap;
      overflow-wrap: break-word;
   }
   <div class="chat">
    <div class="messages">
      <div class="spacer-box"></div>
      <div class="message">1</div>
      <div class="message">2</div>
      <div class="message">3</div>
      <div class="message">4</div>
      <div class="message">5</div>
      <div class="message">6</div>
      <div class="message">7</div>
      <div class="message">8</div>
      <div class="message">9</div>
      <div class="message">10</div>
      <div class="message">11</div>
      <div class="message">12</div>
      <div class="message">13</div>
      <div class="message">14</div>
      <div class="message">15</div>
      <div class="message">16</div>
      <div class="message">17</div>
      <div class="message">18</div>
    </div>
    <div class="footer">
      <div contenteditable role="textbox" id="chat-input"></div>
    </div>
    <div>

도움이 될 수 있으면 좋겠는데:)

언급URL : https://stackoverflow.com/questions/34213227/scrollable-div-to-stick-to-bottom-when-outer-div-changes-in-size

반응형