Build a Simple Chat App with Node.js and Socket.io
Paolo
Posted on March 22, 2020
Are you trying to find a way to build a chat app but you struggles to find a simple tutorial that is gonna to explain everything?
This is the right article for you!
This is a preview of what we’ll building
Disclaimer
In this article I’ll cover the logic that are behind the chat and I won’t cover all the styling part. If you need the css file I’ll put the link of the github repo at the end of the article.
Setup
I assume that you have npm and node installed and knows how they worked (at least the basics). A basic Knowledge of Javascript is required.
So Let’s get started.
Create a directory for the application, open the directory with your favourite editor:
mkdir chatApplication && cd client
Next, let’s initialize the directory as a Nodejs application.
npm init -y
Now let’s start to install all the dependencies that we’ll need to build this app:
npm i express randomcolor socket.io uuid
And as a dev dependency we will install nodemon that is a utility that will monitor for any changes in your source and automatically restart your server.
npm i -D nodemon
Building the Server side
We’ll keep the server side logic in the app.js file( located in the main folder), that is a simple express server
//app.js
const express = require('express');
const app = express();
let randomColor = require('randomcolor');
const uuid = require('uuid');
//middlewares
app.use(express.static('public'));
//routes
app.get('/', (req,res)=>{
res.sendFile(__dirname + '/client/index.html');
});
//Listen on port 5000
server = app.listen( process.env.PORT || 5000);
Now that we have our server ready, let’s start working on the socket.io logic. First of all we need to instantiate socket.io, after that of io will listen on every connection. When the user is connected our socket will listen to the following events:
- change_username : get the user username and sending it
- new_message: listen the new message for the user
- disconnect: listen when a user is disconnected from the chat
So let's write the code to make it work!
//app.js
//socket.io instantiation
const io = require("socket.io")(server);
const users = [];
const connnections = [];
//listen on every connection
io.on('connection', (socket) => {
console.log('New user connected');
//add the new socket to the connections array
connnections.push(socket)
//initialize a random color for the socket
let color = randomColor();
//Set the first username of the user as 'Anonymous'
socket.username = 'Anonymous';
socket.color = color;
//listen on change_username
socket.on('change_username', data => {
let id = uuid.v4(); // create a random id for the user
socket.id = id;
socket.username = data.nickName;
users.push({id, username: socket.username, color: socket.color});
updateUsernames();
})
//update Usernames in the client
const updateUsernames = () => {
io.sockets.emit('get users',users)
}
//listen on new_message
socket.on('new_message', (data) => {
//broadcast the new message
io.sockets.emit('new_message', {message : data.message, username : socket.username,color: socket.color});
})
//Disconnect
socket.on('disconnect', data => {
if(!socket.username)
return;
//find the user and delete from the users list
let user = undefined;
for(let i= 0;i<users.length;i++){
if(users[i].id === socket.id){
user = users[i];
break;
}
}
users.splice(user,1);
//Update the users list
updateUsernames();
connnections.splice(connnections.indexOf(socket),1);
})
})
If you are wondering what randomColor() is for you will find out when will cover the front-end side
Front-end side
So the front-end is pretty easy and its only consists in an html file and two javascript files( and the css file that I’m not gonna cover in this tutorial).
So let’s go in the client folder and create the following file: index.html, chat.js, modalScript.js and the css folder where we gonna keep the style.css.
The index.html when its first render it has a modal to get the user name, and for the other stuff i use flexbox for make it more responsive:
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" const="text/html;charset=UTF-8" />
<link href="http://fonts.googleapis.com/css?family=Comfortaa" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/style.css" >
<!--Socket.io scirpt-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<title>Simple Chat App</title>
</head>
<body>
<!-- The Modal -->
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<h1 class="modal-title">What's your nickname?</h1>
<input id="nickname-input" class="custom-input" type="text" />
</div>
</div>
<!--Big wrapper-->
<div class="big-wrapper">
<!-- Left Column-->
<div class="online-user-wrapper">
<div class="online-user-header-container">
<header>
<h2>Online Users</h2>
</header>
</div>
<div>
<!--Online users goes here-->
<ul id="users-list">
</ul>
</div>
</div>
<!--Chat Wrapper -->
<div class="chat-wrapper">
<div class="super-chat-title-container">
<header>
<h1>Chat</h1>
</header>
</div>
<!--Messages container-->
<div id="chatroom">
<!--x is typing goes here-->
<div id="feedback"></div>
</div>
<!-- Input zone -->
<div id="input_zone">
<input id="message" class="vertical-align custom-input" type="text" />
<button id="send_message" class="vertical-align btn" type="button">Send</button>
</div>
</div>
</div>
<!--jQuery script-->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<!--Scripts-->
<script src="./chat.js"></script>
<script src="./modalScript.js"></script>
</body>
</html>
If we leave the index.html it as it is the modal won't go away, so we need to add some javascript to handle it.
//modalScript.js
// Get the modal
var modal = document.getElementById("myModal");
const nicknameInput = document.getElementById("nickname-input");
// Close modal when nick-name is typed
nicknameInput.onkeypress = e => {
let keycode = (e.keyCode ? e.keyCode : e.which);
if(keycode == '13'){
modal.style.display = "none";
}
};
If the backend is listening for events so the frontend have to send these events. We'll send these events thought chat.js file. For instance when a new user enters in the app we have to listen for it and get the username and send it to the backend.
//chat.js file
$(function () {
//make connection
let socket = io.connect('http://localhost:5000');
//buttons and inputs
let message = $("#message");
let send_message = $("#send_message");
let chatroom = $("#chatroom");
let feedback = $("#feedback");
let usersList = $("#users-list");
let nickName = $("#nickname-input");
//Emit typing
message.bind("keypress", e => {
let keycode = (e.keyCode ? e.keyCode : e.which);
if(keycode != '13'){
socket.emit('typing')
}
});
Messages
As you guessed for the message is the same principle!
//chat.js
$(function () {
//make connection
let socket = io.connect('http://localhost:5000');
//buttons and inputs
let message = $("#message");
let send_message = $("#send_message");
let chatroom = $("#chatroom");
let feedback = $("#feedback");
let usersList = $("#users-list");
let nickName = $("#nickname-input");
//Emit message
// If send message btn is clicked
send_message.click(function(){
socket.emit('new_message', {message : message.val()})
});
// Or if the enter key is pressed
message.keypress( e => {
let keycode = (e.keyCode ? e.keyCode : e.which);
if(keycode == '13'){
socket.emit('new_message', {message : message.val()})
}
})
//Listen on new_message
socket.on("new_message", (data) => {
feedback.html('');
message.val('');
//append the new message on the chatroom
chatroom.append(`
<div>
<div class="box3 sb14">
<p style='color:${data.color}' class="chat-text user-nickname">${data.username}</p>
<p class="chat-text" style="color: rgba(0,0,0,0.87)">${data.message}</p>
</div>
</div>
`)
keepTheChatRoomToTheBottom()
});
//Emit a username
nickName.keypress( e => {
let keycode = (e.keyCode ? e.keyCode : e.which);
if(keycode == '13'){
socket.emit('change_username', {nickName : nickName.val()});
socket.on('get users', data => {
let html = '';
for(let i=0;i<data.length;i++){
html += `<li class="list-item" style="color: ${data[i].color}">${data[i].username}</li>`;
}
usersList.html(html)
})
}
});
});
// function thats keeps the chatbox stick to the bottom
const keepTheChatRoomToTheBottom = () => {
const chatroom = document.getElementById('chatroom');
chatroom.scrollTop = chatroom.scrollHeight - chatroom.clientHeight;
}
Typing
In order to make our chat app complete we need to add a functionality that all the chat apps have: send which user is typing something. To implement it, is quite easy.
In our chat.js file we have to emit when a you are typing and listening when other others users are typing.
Let's add these few lines of code:
//Emit typing
message.bind("keypress", e => {
let keycode = (e.keyCode ? e.keyCode : e.which);
if(keycode != '13'){
socket.emit('typing')
}
});
//Listen on typing
socket.on('typing', (data) => {
feedback.html("<p><i>" + data.username + " is typing a message..." + "</i></p>")
});
While in the app.js file we wanna to broadcast we a user is typing. We do it simplying add these three lines of code:
//listen on typing
socket.on('typing', data => {
socket.broadcast.emit('typing',{username: socket.username})
})
Conclusion
So that’s the end our app. As you can see is pretty simple and it doesn’t require a tons of code as I thought before building it.
Feel free to reports issues and bugs and improve this code, thus making it better.
That’s the Github repo:
paolodelia99 / Simple-node-chat-app
A simple chat app built with node.js and socket.io
Posted on March 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.