Real-time chat app using WebSockets (part 2)

Rabindra Joshi
8 min readFeb 13, 2018

--

This is a second part of chat app with WebSockets. If you accidentally stumbled here, start from this tutorial.
This is the second and last tutorial of the series. We’re going to set up the WebSockets and create a complete real time chat app.

A quick recap:
We created an express server on Node. We also served the public files, and one of those files was index.js. I also added some css to style.css. The css file is available on the GitHub repository of frost-chat or on this gist.

Fire up the project from the last tutorial.

λ  node index

Here’s how it looks.

Alright, let’s get back to the work!

We’re going to use a library called socket.io to work with . It makes our job really easy.
We’re going to set up the socket.io in front-end and back-end of our app. When we request our index.html file, socket.io is going to be set up in there. It’s also going to be on index.js. Then we’re going to establish the socket connection between the two and pass data them.

λ npm install socket.io --save

Use it on ndex,js file using

var socket = require("socket.io")

Setup the scoket with server as a parameter

var io = socket(server);

What this does is it sets up the socket coennction on the expressserver we created in the last tutorial.
Now socket.io will wait for the connection from the client and set up a web socket from two.

We can listen when the connection is made

io.on("connection", function(){
console.log("Socket connection made);
});

It can also take the instance of a particular socket connection as a parameter as wer’re going to have multiple connections. Let’s call that variable a socket.

io.on("connection", function(socekt){
console.log("Socket connection made. ");
});

We’ll deal with this socket object later.

We still have nothing different in the the browser. And it won’t log anything on the console.
It will only log on connecting.

Let’s make a connection from the front-end part of the app.
Youcan grab the socekt.io library from here

Paste it in the index.html file.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>

I’ll also create a chat.js file in the public directory and link it at the bottom of the index.html. This will be used for our custom script.

<script src="/chat.js"></script>

We’ll establish the connection from this chat.js to the server to create a web socket between two.
Since the library is already loaded before chat.js, we have access to variables from socket.io library.

//Create  connection
var socket = io.connect("http://localhost:9090");

This socket variable has nothibng to do with the same variable in index.js. This one is at the front-end and earlier one was for the backend.

index.js:

io.on("connection", function(socekt){
console.log("Socket connection made");
});

Now reload and the connection message will be logged.

This happened because we connected from front-end using socket.io library to the backend. Now, we have successfully created a web-socket.

On index.js, log the socket.id

io.on("connection", function(socekt){
console.log("Socket connection made "+ socekt.id);
});

You’ll see that the random string logged, which is a socket is. Refresh the browser and you’ll get a different socket connection everytime.

You get a unique socket connection for each clients around the globe.
There’s new socket for every connection.

Now we have the connection ready, we’re ready to communicate between the server and clients.

What is happening?
When we request index.html file, the socket is set up there. And the socket is already set up at backend through index.js when the server starts. This forms the socket connection between server and client.

Now, we can start emitting messages using socket.io.

Client sends the data through web socket to Server. The server receives it and emits the data to all other clients through web sockets. We have to display the received information in the client.

In the index.html file

  • Remove the Hello World.
  • Create container with id frost-chat
  • inside frost-chat, create another container with id chat-window
  • Inside chat-window, create another container with id output

frost-chat has everything, chat-window display the all components of chat, and output display the chat sent from client and emitted by server.

We also need input fields for user handle and message. To send the message the send buutton is also required.

Look at the code below.

<!--head content-->
<body>
<div id="frost-chat">
<div id="chat-window">
<div id="output">
<!--Display message here-->
</div>
</div>
<input type="text" id="handle" placeholder="Handle">
<input type="text" id="message" placeholder="Message">
<button id="send">Send</button>
</div>
<script src="/chat.js"></script>
</body>
<!--ending other tags-->

I have already populated the css file with proper styles for all these ids and tags at the end of the last tutorial.

This is how it looks now:

The send button does (surprise!) nothing.
It’s time for chat.js to do the front-end magic.
We'll emit the message and send to the server on hitting the send button. Let's add this functionality.

Start querying the DOM and start storing variables. We need to store the message, handle and also have the control over output and send.

Here’s how to query the DOM (from chat.js)

var message = document.querySelector("#message");
var handle = document.querySelector("#handle");
var btn = document.querySelector("#send");
var output = document.querySelector("#output");

Verify that you don’t get errors with the selectors.
I used the simple console window to check the variables, all must exist with content simlar to one below.

We can interact with the DOM through these variables.

Emit an event when a user clicks send:
We want to emit the message so that others can receive.
This is vanilla javascript to send it down the websocket.

socket.emit() is how you emit the message. It needs 2 parameters. The first one is the name of the message and second one the message itself.
The message will be an object with handle so that we know and display sender's name and message to display the actual message. Let's call our message name chat.

socket.emit("chat", {});

We want to emit message on click.

btn.addEventListener("click", function () {
socket.emit('chat', {
message: message.value;
handle: handle.value;
});
});

We get the handle and message from the value in our query selector. These are the values in the input field.

This will send the data to the server from a client.
But the server is doing nothing with the data, so other clients won’t get the message.

We need to handle this message on the server.
The socket is connected at the io.on(). Add this code inside that function. We'd like to fire up a callback function when we receive a chat message. The chat message comes with data which we pass to to it.

socket.on("chat", function (data) {
//
} );

The socket in socket.on refers to a particular socket connection.

Inside the callback function we’d want send the message to all websockets connected.
io.sockets refers to all the sockets connected.

io.sockets.emit("chat", data);

Here’s how io.on is now (in index.js).

io.on("connection", function(socekt){
console.log("Socket connection made "+ socket.id);
socket.on("chat", function (data) {
io.sockets.emit("chat", data);
} );
});

What’s happening?
The client sends a message to the server, and then server emits the same message to all other clients connected.

The server receives the data from client successfully.
The server also sends the data but it is not handled by the clients.

Let’s work on that issue.

Listen for the chat message on the client:

socket.on("chat", function (data) {
<!-----Handle and Message Here---!>
});

Here’s how the message and handle content is rendered. The output is appended with each message.:

output.innerHTML += "<p><strong>" + data.handle +":</strong>" + data.message + "</p>"

Here’s how it’ll render as:

John: Hi. I am John.

What just happened?

The server is listening to all sockets at all times. Multiple clients connect through web sockets.
Whenever a client emits a message with a handle name, the server receives it and also emits the same to all clients.
The clients receive the emitted message and display in a proper format.
This is all.

Now, we have a functioning real-time chat app.

“John is typing…” would be a nice addition to this chat app.

Let’s implement that too!

This is will be using broadcasting. It is very similar to emitting. Broadcasting will not send the message to the sender.
It won’t make sense for John to look at “John is typing…”Clients other than John will see that he is typing.

We need a container to put the feedback message. Add the feedback container just below output. Note that the CSS for this id was created beforehand. If you’re following along for the exact same output, the code is here at the GitHub repo.

<div id="output">
<!--Display message here-->
</div>
<div id="feedback"></div>
</div>

The typing message will be displayed in the feedback.
We need to get hold of this thing on chat.js.

Add

var feedback  = document.querySelector("#feedback");

We’ll attach an event listener to the message field, using a keypress event.

Attach the event listener to message on chat.js.

message.addEventListener("keypress", function () {
socket.emit("typing", handle.value);
});

This will send the tyoing message to server.
Now, we’ll have the server to send the broadcast message to all clients.

Undet the io.on(), add the following:

socket.on("typing", function (data) {
socket.broadcast.emit("typing", data);
});

Yes, this is similar to another connection we added a little ago. This listens for typing and broadcasts instead of emitting using socket.broadcast.emit("typing", data);.

Here’s the whole function io.on function:

io.on("connection", function(socket){
console.log("Socket connection made "+ socket.id);
socket.on("chat", function (data) {
console.log(data);
io.sockets.emit("chat", data);
});
socket.on("typing", function (data) {
console.log(data);
socket.broadcast.emit("typing", data)
});
});

Now, we have the data broadcasted from server, we have to handle it on the client.

Listen for the typing message on client.

socket.on("typing", function (data) {
//feedback message here
});

The feedback message is, again, HTML. It is attached to the #feedback.

feedback.innerHTML = "<p> <em>" + data + " is typing a message...</em> </p>";

It’ll look like:

John is typing a message…

Here’s the app in action.

The small in the app is that it’s always there once you start typing, even after sending.

We want to solve this issue.
This can be solved by resetting the feedback.innerHTML to blank on sending the message.

The message sent during the display of the message is chat.
Set the feedback blank.

socket.on("chat", function (data) {
output.innerHTML += "<p><strong>" + data.handle +":</strong>" + data.message + "</p>";
feedback.innerHTML = "";
});

The “Typing…” message is gone once you hit the send button.

That is all for now, there’s a lot you can extend on this application. Happy learning.

The GitHub repository for this project is here.
Happy learning!
If you have questions, reach me on GitHub, Facebook or
Twitter.

original publish on Nodefrost.com

--

--