JSFiddle - React, Tailwind, and code Playground

by bizamajig

HTML

<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>

CSS

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

JavaScript

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