JSFiddle - React, Tailwind, and code Playground
HTML
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Group Chat</title>
<link rel="icon" href="favicon.png" type="image/png">
<link rel="stylesheet" href="chatstyles.css">
</head>
<body>
<div class="container clearfix">
<div id="avatars" style="display:none">
<p>Choose your avatar..</p>
<div id="image-list"></div>
</div>
<div id="chat" style="display:none">
<div class="chat-header clearfix">
<div id="current-user"> </div>
</div>
<div id="chat-history">
<ul id="message-window"></ul>
</div>
<div class="chat-message clearfix">
<form onSubmit="javascript:return messageSubmit()">
<textarea name="message-to-send" id="message-send" placeholder="Type your message" rows="3"></textarea>
<button>Send</button>
</form>
</div>
</div><!-- end chat -->
</div><!-- end container -->
<script>
var audio = new Audio('sounds/ping.mp3');
var chatHistoryWindow = document.getElementById('chat-history');
var messageWindow = document.getElementById('message-window');
var messageTextArea = document.getElementById('message-send')
function addMessage(person, message) {
var chatUser = getChatUser(); // current user
if (chatUser == person){
displayOwnMessage(person, message);
}
if (chatUser != person){ // other user
displayOthersMessage(person, message);
}
chatHistoryWindow.scrollTop = chatHistoryWindow.scrollHeight;
}
function displayOwnMessage(person, message) {
var ownTemplate = '<li class="clearfix"> \
<div class="my-message float-right"> \
'+message+' \
</div> \
</li>';
messageWindow.innerHTML += ownTemplate;
}
function displayOthersMessage(person, message) {
var otherTemplate = '<li class="clearfix"> \
<div class="message-data"> \
<img src="images/gif/'+person+'.gif"> \
</div> \
<div class="other-message"> \
...
CSS
*, *::before, *::after {
box-sizing: border-box;
}
body {
background: #C5DDEB;
font: 14px/20px sans-serif;
color: white;
}
.container {
margin: 0 auto;
background: #444753;
border-radius: 5px;
}
#avatars {
padding: 10px;
text-align: center;
}
#image-list > img {
width: 12vw;
padding: 2px;
}
@media only screen and (min-width: 961px) {
#image-list > img {
width: 6.5vw;
padding: 2px;
}
}
#message-window {
list-style: none;
padding: 0 10px;
}
#chat {
width: 100%;
float: left;
background: #F2F5F8;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
color: #434651;
}
.chat-header {
padding: 7px 20px 0;
border-bottom: 2px solid white;
text-align: right;
}
#current-user > img {
width: 50px;
height: 50px;
border-radius: 50%;
}
#chat-history {
border-bottom: 2px solid white;
overflow-y: scroll;
height: calc(100vh - 230px);
}
.message-data {
margin-bottom: 15px;
float: left;
}
.message-data > img {
width: 50px;
height: 50px;
border-radius: 50%
}
.message-data-name {
font-size: 80%;
display: block;
color: black;
}
.my-message {
color: white;
padding: 18px 20px;
line-height: 26px;
font-size: 16px;
border-radius: 7px;
margin-bottom: 20px;
width: 80%;
position: relative;
margin-right: 20px;
background: #94C2ED;
}
.other-message {
color: white;
padding: 3px 20px 18px 20px;
line-height: 26px;
font-size: 16px;
border-radius: 7px;
margin-bottom: 20px;
width: 80%;
position: relative;
margin-left: 0px;
background: #86BB71;
}
.my-message::after {
bottom: 50%;
right: 0;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-left-color: #94C2ED;
border-width: 10px;
margin-right: -19px;
}
.other-message::after {
bottom: 50%;
left: 0;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
...
JavaScript
let http = require('http');
let fs = require('fs');
let WebSocket = require('ws');
let wss = new WebSocket.Server({
noServer: true
});
let avatarsFolder = fs.readdirSync('www/images/gif');
var messageDetail;
var chatHistory;
let httpServer = http.createServer((req, res) => {
var url = req.url;
var contentType;
if (url == '/getimages') { // create 'array' of avatar images and names
var jsonAvatars = [];
avatarsFolder.forEach(file => {
jsonAvatars.push({
'name': file.split('.')[0]
})
});
res.writeHead(200, {
"Content-Type": "text/text"
});
res.end(JSON.stringify(jsonAvatars)); //send
return;
}
switch (url.substr(-4)) {
case '.png':
contentType = 'image/png';
break;
case '.gif':
contentType = 'image/gif';
break;
case '.css':
contentType = 'text/css';
break;
case '.mp3':
contentType = 'audio/mpeg';
break
default:
contentType = 'text/html';
}
fs.readFile('/www/' + (req.url == '/' ? 'index.html' : req.url), (err, data) => {
if (err) {
res.end('fs.readFile error: ' + err);
} else {
res.writeHead(200, {
"Content-Type": contentType
});
res.end(data); // send
}
});
}).listen(80);
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
messageDetail = JSON.parse(data); // new chat message
var numberMessages = Object.keys(chatHistory).length;
// add an object after the last one (chatHistory starts at 0)
chatHistory[numberMessages] = {
person: messageDetail.messageUser,
message: messageDetail.messageText
};
writeToChatHistory(chatHistory);
wss.clients.forEach(function each(client) {
...