DEV Community

Cover image for Newbie guide : Code a chat app with socket.IO
Kauress
Kauress

Posted on • Edited on

Newbie guide : Code a chat app with socket.IO

Code a chat app

Guide

  1. Part 1 here
  2. Introduction
  3. The features of the chat app
  4. Socket.IO methods
  5. Socket.IO events
  6. Callback functions
  7. Directory structure
  8. So far..
  9. Set up index.html and style.css
  10. Sending a message from the client to the server
  11. Receiving the message from the client at the server
  12. Displaying the message to all connected clients
  13. Broadcasting "user is typing..." message
  14. Display greeting message when someone joins the chat
  15. Showing total number of users

1. Introduction

Picking up from Part 1, this guide will focus on building a chat app called "Kidda Fer" [What's up in punjabi].

2. Features of the chat app

The features of the chat app in this guide will be:

  1. Greeting message to user upon connection πŸ™‚
  2. User/users send a message in the chat room which is displayed immediately to all users [AKA chatting] πŸ’»
  3. When a user is typing a message, the server broadcasts a: "User is typing...." message to all the other users ⌨️
  4. Display the number of connected users in a panel 🌎

3. Socket.IO methods

The socket object uses socket.IO to keep track of a given socket connection at any particular instance. The socket object has methods and properties that you can access and use.

Objects are a collection of properties i.e. key value pairs. A property can be described as a variable associated with the object that can reference any data type (example strings, numbers, booleans etc). A method is a function of an object, in our case it is the socket object.

Some examples of socket methods and properties are:

Methods Properties
socket.emit( ) [emit the event to ALL the connected clients] socket.id [access the unique id of the socket connection]
socket.join( ) [subscribes a socket to a given chat room] socket.connected [returns true or false]
socket.send( ) [sends messages which are received with the 'message' event] socket.disconnected [returns true of false]
socket.on( ) [This method takes an eventName and callback function as parameters)] socket.customProperty [set a custom property on the socket object]

Newbie note: Notice that a socket method is recognized by a parenthesis "( )", whereas you simply access the socket object's properties via the dot notation.

Lets have a look at socket.IO properties:

console.log(socket.connected);
console.log(socket.id);
console.log(socket.disconnected);
Enter fullscreen mode Exit fullscreen mode

returns:

true
CYpR8HOx2dECnJy0AAAA
false
Enter fullscreen mode Exit fullscreen mode

These socket.IO methods take 2 arguments:

  • name of the event
  • callback function

Let's move on to discussing socket.IO events.

4. Socket.IO events

As this is a chat app we are guaranteed to have 'events' such as connecting, disconnecting, reconnecting or even joining a particular chat room within the main channel.

Since socket.IO provides both a server and client side API we have to take care of an event on both sides.

Take for example our code in index.js from the previous tutorial wherein, we created a server and :

//declare var io which is a reference to a socket connection made on the server
var io= socket(server);

//Then use the io.on method which looks for a connection
//upon a connection execute a callback function which will console.log something
io.on('connection', function(){
  console.log('made socket connection');
});

Enter fullscreen mode Exit fullscreen mode

The io.on event 'handles' connection. In this case we are referencing any connections initiated on the server side with var io. And on a "connection" event we want to run a callback function which will console.log the string:made socket connection

Fundamentally 'emit' and "on" methods are responsible for 'chatting'. This is by sending messages via the emit method and listening to emitted messages with the 'on' method.

There are reserved server and client side events. Some of these are:

Server-side event Client-side events
Connect Connect
Reconnect Disconnect
Join/Leave
Reconnect

The syntax is such that it seems you are listening to and triggering events. These events are handled by socket.IO server and client side methods.

5. Callback Functions

As stated above socket.IO methods take an event and a callback function as arguments. If you'd like to know what callback functions are you may read this little worksheet here.

For us in essence a callback function is one which is triggered in response to some event such as a "connection" or "disconnect" event.

6. Directory structure

Your directory structure will look like this. The same as from Part 1.
chat_app
β”œβ”€β”€ node_modules
β”œβ”€β”€ public
β”‚ └── index.html
β”‚ └── style.css
β”‚ └── chat.js
β”œβ”€β”€ index.js
β”œβ”€β”€ package.json

The files we'll primary be working with are index.js which contains our server code and chat.js which contains the client side code.

7. So far..

In the last tutorial, we set up all our dependencies, used express.js to make a server, included a reference to socket.IO library in index.html and then set up socket.IO on both the server and client sides by requiring it.

Your code should look like this so far:

Note: I previously used 'var' instead of const

index.js

const express = require('express');
const socket = require('socket.io')
let clients = 0;

const app = express();
const server = app.listen(4000, function(){
    console.log('listening for requests on port 4000,');
});

app.use(express.static('public'));

const io= socket(server);

Enter fullscreen mode Exit fullscreen mode

chat.js


const io= socket(server);

io.on('connection', function(){
  console.log('made socket connection');
});


Enter fullscreen mode Exit fullscreen mode

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Newbie Guide</title>
       <script src="/socket.io/socket.io.js"></script>
        <link href="/style.css" rel="stylesheet" />
    </head>
    <body>
        <h1>Socket.io</h1>
    <script src="/chat.js"></script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

8. Set up index.html and style.css

Set up index.html as so:

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <meta name="description" content="Chat">
      <meta name="keywords" content="HTML,CSS,JavaScript,SOCKET.IO">
      <meta name="author" content="Kauress">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>#KIDDAFER</title>
      <script src="/socket.io/socket.io.js"></script>
      <!-- Latest compiled and minified CSS -->
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
      <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet">
      <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
      <link href="/style.css" rel="stylesheet" >
   </head>
   <body>
      <div class="container-fluid header-container px-0">
         <div class="row mx-0">
            <div class="col-sm-12 px-0">
               <div class="row">
                  <div class="col-sm-2">
                     <h4 class="header-text">#hundas</h4>
                  </div>
                  <div class="col-sm-4">
                     <br> <br>
                     <h1 class="header-text">Kidda Fer?</h1>
                  </div>
               </div>
            </div>
            <!-- end of col-sm-12 -->
         </div>
         <!-- end of row -->
      </div>
      <!-- end of container> -->
       <div>
       <p id="feedback"></p>
      </div>
      <div class="container-fluid" id="output-container">
         <div class="row no-gutters">
            <div class="col-sm-2 side" id="left-panel"></div>
            <div class="col-sm-8" id="main-output">
               <div class="row output-row no-gutters">
                  <div class="col-sm-12"id="output">
                     <p class="announcements"></p>
                  </div>
               </div>
               <!-- end of row -->
               <div class="row no-gutters">
                  <div class="col-sm-6">
                     <textarea id="message" type="text" placeholder="Message"></textarea>
                  </div>
                  <!-- end of col-sm-6-->
                  <div class="col-sm-6 no-gutters" id="action-here">
                     <input id="handle" type="text" placeholder="Handle" />
                     <input id='fileid' type='file' hidden/>
                     <input id='file-button' type='button' value='+' />
                     <input id='gif-button' type='button' value='GIF' />
                     <button class="btn-btn-success btn-block" id="send">Send</button>
                  </div>
                  <!--end of col-sm-12 -->
               </div>
               <!-- end of nested row -->
            </div>
            <!-- end of col-sm-8 -->
            <div class="col-sm-2 side" id="right-panel"></div>
         </div>
         <!-- end of row -->
      </div>
      <!-- end of container -->
      <script src="/chat.js"></script>
      <!-- jQuery library -->
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <!-- Latest compiled JavaScript -->
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
   </body>
</html>

Enter fullscreen mode Exit fullscreen mode

Set up style.css as such:

@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
body{
  font-family: Montserrat, sans-serif;
  color: #FFFFFF;
  background-color: #23272A;
  overflow-x: hidden;
}

.header-container{
  background-image: url("images/kidda.png");
  height:150px;
  border-top: 3px solid #23272A;

}
.header-text{
  text-transform: uppercase;
  font-weight: 900;
  opacity: 0.7;
}

#main-output{
  background-color: #2C2F33;
}
#output{
  height: 450px;
  overflow-y: scroll;
  background-color: #2C2F33;
  border-bottom: 3px solid #23272A;
}

#message {
    width: 100%;
    height: 100px;
    background-color:#2C2F33;
    color: #FFFFFF;
    border: 3px solid #2C2F33;
    overflow:auto;
}
.fa-smile-o{
  color: #FFFFFF;
}
#action-here{
  border-left: 5px solid #23272A;

}
#file-button{
  background-color: #7289DA;
  color: #FFFFFF;
  width: 30px;
  height: 30px;
  border-radius: 30px;
  border: none;
}
#send{
  background-color: #7289DA;
  border: none;
  opacity: 0.7;
}

#handle{
  width: 70%;
  background-color:#2C2F33;
  opacity: 0.5;
  border: none;
  height: 30%;
  color:#FFFFFF;
}
#date{
font-style: oblique;
color:#99AAB5;
font-size: 14px;
}

#style-handle{
  color: #7289DA;
}
.announcements{
    color: #7289DA;
    text-transform: full-width;
}
#right-panel{
  padding-top: 3px;
  text-align:center;
  color: #7289DA;
  border-top: 2px solid #7289DA;
}
#left-panel{
  padding-top: 3px;
  text-align:center;
  color: #7289DA;
  border-top:2px solid #7289DA;
}
/*
#7289DA
#FFFFFF
#99AAB5
#2C2F33
#23272A
*/
Enter fullscreen mode Exit fullscreen mode

9. Sending a message from the client to the server

Now let's start with the actual chatting part..

In chat.js what we're going to first do is to query DOM elements from index.html and create references for them.
Just below const socket= io.connect('http://www.localhost:4000') typing the following:

const socket = io.connect('http://localhost:4000');

// Query DOM elements
const message = document.getElementById('message');
const handle = document.getElementById('handle');
const sendButton = document.getElementById('send');
const output = document.getElementById('output');
const announcements = document.querySelectorAll('.announcements');
const feedback = document.getElementById('feedback');
const rightPanel = document.getElementById('right-panel');

//create date object
const date = new Date().toDateString();


Enter fullscreen mode Exit fullscreen mode

  1. const message references the DOM textarea element wherein the user types a message.
  2. handle is the input element where the user will type in their chat handle
  3. sendButton is well you guessed it, the send button.
  4. const output is the div where the chat messages will be outputted to on the screen.
  5. const announcements references all elements with the class of 'announcements', and this will display announcements such as when a user joins the chat.
  6. const feedback references the div with id of 'feedback' will display the message: "User is typing a message...".
  7. const rightPanel references the div with class of right-panel which will display the total number of users in the chatroom
  8. We also create a new date object as we will display the date as a string and this will be referenced by const date

Now what we want should happen is that, when a user types in their handle and a message in the and clicks on the 'send' button, the message should be emitted to the server to be received. The server in turn will send the message to all clients.

Continuing in chat.js

So if the length of the message and length of the handle is > 0 we want to send the chat messaging using the "emit" method. The reason we check for the length property of both message and handle is so that users aren't spamming by sending empty chat messages.
The emit method will send a message down the socket to the server. It takes 2 arguments:

  1. The name of the message event, whatever you choose to call it. We have called it 'chat'

  2. The data-value of 'chat' is the chat message input. We send
    an object along with the emit method which is a JavaScript object with the following key:value pairs:

    • message: message.value which is the value of the textarea element
    • handle: handle.value which is the handle input value
sendButton.addEventListener('click', function(){
  /*make sure user does not send an empty message with an empty handle which is annoying and spammy*/
   if(message.value.length > 0 & handle.value.length > 0){
  socket.emit('chat', {
      message: message.value,
      handle: handle.value
  });
}
//once the message is sent, reset the innerHTML of the message div to an empty string
  message.value = "";
});

Enter fullscreen mode Exit fullscreen mode

Now lets receive the 'chat' message on the other side which is the server.

10. Receiving the message from the client at the server

In index.js we will receive the 'chat' message that was 'emitted' on the client side. What we want to do is to not only receive the 'chat' message but to also emit it to all connected clients. We will do it inside the callback function which is called when a socket connection is established with the client.

socket.on("chat",function(data){
    io.sockets.emit("chat",data)
  });
});//main

Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. socket' refers to that particular 'socket connection' established with a client.
  2. We are using the 'on' method that will listen for the 'chat' event and fire a callback function
  3. The function takes 'data' as a parameter and will receive the data that we sent.
  4. We send out that chat message with io.sockets.emit - in this case io.sockets refers to all connected clients.
  5. And once again sending the 'chat' message event along with the data received from the first client which is the 'data' object as the 2nd parameter.

11. Displaying the message to all connected clients

So we sent a message from the client to the server. The server then received the message and sent it to all the clients connected to the server. This includes the original sender of the message.

But we still have to display the message sent from the server to all connected clients. The way to do this is to go back to chat.js and simple receive the 'chat' message and display it using the innerHTML property of the display output element.

In chat.js

socket.on('chat', function(data){
   feedback.innerHTML = '';
  output.innerHTML += '<p>'+ '<span id="date">' + date  + "  " + '</span>' + '<span id="style-handle">' + data.handle + '  :   ' + '</span>'  + data.message + '</p>';
});

Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. socket refers to const socket so that individual socket for the client
  2. Once again using the on method to listen for the 'chat' event fired back from the server
  3. And upon the 'chat' event we fire a callback function which takes data as a parameter
  4. Don't worry about feedback.innerHTML for now..
  5. Inside the callback function we can do something with the data received. So display the data object received which has the handle and message keys

12. Broadcasting messages to connected clients

What is a broadcast event? Well when the server broadcasts a message it will send it to every client down the socket connection except the client that sent the message in the first place.

Now what we will do it broadcast a "user is typing a message" to all other users when user 'A' is typing a message.

In chat.js

message.addEventListener('keypress', function(){
  if(handle.value.length > 0){
    socket.emit('typing', handle.value);
  }
});
Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. Attach the addEventListener method to the message variable which references the textarea element in index.html
  2. The event listener "listens" for a keypress event
  3. When the keypress event occurs you will run a callback function
  4. The callback function will emit a 'typing' event to the server along with the user's handle (handle.value) if handle.value.length is > 0 (i.e. a user actually inputted their username)

The server in turn will receive the emitted message. And then broadcast the message to all clients except the client who emitted the 'typing' event.

In index.js:

Inside the main connection function socket.on('chat'..)

  // Handle typing event
   socket.on('typing', function(data){
    socket.broadcast.emit('typing', data);
 });

Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. Create another socket.on method that listens for 'typing' event
  2. When the event occurs a callback function runs which takes 'data' as an argument
  3. The 'data' in this case is the user's handle.value
  4. We then want to broadcast a message to all connected clients
  5. Once again socket refers to the individual socket connection created between the server and client
  6. The broadcast.emit method will send the 'typing' event and data which is handle.value

Now let's work on the client side which will receive the 'typing' message broadcasted from the server.

In chat.js

socket.on('typing', function(data){
    feedback.innerHTML = '<p><em>' + data + ' is typing a message...</em></p>';
});

Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. socket refers to that particular socket connection between the client and server
  2. Using the on method
  3. The first argument of the on is the typing event
  4. Upon the typing event we will run a callback function which takes data as a parameter
  5. And inside the function you will do something with the 'data'
  6. And in this case we will change the innerHTML property of the feedback element to data + ' is typing a message...'

13. Showing total number of users and sending users a "Welcome" message

In this section we will:

  1. Display the total number of chat users in the panel to the right of the main chat box
  2. Display a greeting to the user when they are on the chat page

In index.js, declare clients which will keep track of the total number of clients

const express = require('express');
const socket = require('socket.io')
let clients = 0;

Enter fullscreen mode Exit fullscreen mode

And above the main socket.on..connection function, type the following:

socket.emit("message", {
greeting: "Hi there! Remember, choose your handle! "
  });
clients++;
 socket.broadcast.emit('newClientConnect',{ description: clients + ' clients connected!'});
 socket.emit('newClientConnect',{ description: clients + ' clients connected!'});

 socket.on('disconnect', function () {
    clients--;
    socket.broadcast.emit('newClientConnect',{ description: clients + ' clients connected!'});

 });
Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. When a socket connection is established we will use the emit method
  2. The method takes an event to be received at the client side as a an argument. This event is called 'message'
  3. In response to the 'message' event some data i.e. an object will be emitted
  4. The object has the "greeting" key whose value is the string: 'Hi there! Remember, choose your handle!'
  5. After which you will increment the client counter by 1 with clients++
  6. Then you will use the emit and broadcast.emit methods to send a 'newClientConnected' message
  7. The message will contain the number of clients connected and a string: description: clients + ' clients connected!'
  8. Upon a disconnection event, socket.on will run a callback function
  9. The callback function will decrement clients by 1 with clients--
  10. And in case of a 'disconnect' event we will update the newClientConnected message to show the updated number of clients

Phew! Now lets receive this message on the client side!

In chat.js

socket.on('message',function(data){
   announcements[0].innerHTML+= data.greeting;
});

socket.on('newClientConnect',function(data) {
  rightPanel.innerHTML= data.description;
 });
Enter fullscreen mode Exit fullscreen mode

What's happening?

  1. The socket.on method receives event which in turn triggers a callback function that takes data as an argument
  2. We then change the innerHTML of the element at index[0] (since we are iterating over DOM elements with the class of 'announcements'
  3. The innerHTML includes the greeting: 'Hi there! Remember, choose your handle!'
  4. Then the socket.on method receives newClientConnect event which in turn will run a callback function
  5. The function which takes data as an argument will display the total number of clients connected at any time

Top comments (9)

Collapse
 
sahilatahar profile image
Sahil Atahar

Nice article, I have build a Realtime code editor project using socket.io called Code-Sync.

It offers a real-time collaborative code editor featuring unique room generation, syntax highlighting, and auto-suggestions. Users can seamlessly edit, save, and download files while communicating through group chat

Collapse
 
anshika056 profile image
Madan Anshika

hey, after adding the sockect.connect i am not getting the connection console.log

Collapse
 
kauresss profile image
Kauress

hi, what does the error in the console say?

Collapse
 
anshika056 profile image
Madan Anshika

it is likely not sending the msg and nor connecting

Thread Thread
 
kauresss profile image
Kauress

make sure you’re connecting to the correct local host address on both server and client side for the sockets to connect

Thread Thread
 
anshika056 profile image
Madan Anshika

hey thank you for your reply my error got solved thanks

Collapse
 
vishalkk1997 profile image
VishalKK1997 • Edited

Can you please provide the github link for the project

Collapse
 
m121 profile image
Mateo Perez Salazar

Oh I got lost by tutorial, do you have full pages so I can see what is the problem? I think I write bad the order of functions

Collapse
 
kauresss profile image
Kauress

you can print out the article or save as pdf