All Posts

Creating Interactive Websites With Socket.io

Disclaimer: This tutorial assumes you have Node.js > 8.0 installed on your machine. I use yarn in this tutorial to install dependencies however, you can accomplish the same thing with npm.

What Are We Building and Why?

Users expect applications and websites to be interactive and give immediate feedback about tasks being performed. A common occurence in web development is the need to perform a task in the background and at it’s point of completion provide the user who launched the task with feedback. This is especially useful for tasks that are long-running and so they must be offloaded to background processes or for scheduled jobs.

In this tutorial we will be building a very simple web page that provides interactive alerts or “toasts” to the user when background tasks have been performed. To accomplish this, I’m using Socket.io and Express.

Project Setup

Firstly, let’s begin by setting up our project. We can do so with just a couple of files:

project/
|   index.js    
|   index.html  
|   static/
|   |   styles.css    

Next we need to add our dependencies via npm or yarn:

yarn add express nodemon socket.io

After we have our dependencies installed, we’re going to start out by setting up a very simple web page where we will be sending our toast notifications. For the sake of this tutorial we’re going to keep the styles and elements to a minimum to maximize comprehension.

Begin by copy pasting the following into your index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO Live Toasts Example</title>
    <link rel="stylesheet" type="text/css" href="static/styles.css">
  </head>
  <body>
    <div id="container">
      <h1>User Details</h1>
      <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Consectetur dolorum explicabo quam dolores vero. Unde corrupti ea aut soluta modi, dolorum ex eius dignissimos? Vel necessitatibus tempore atque similique maxime!</p>
      <div id="socketid" class="toast"></div>
      <div id="globalToast" class="toast"></div>
      <div id="userToast"></div>
      <button id="update">Update Settings</button>
    </div>
    <script src="/socket.io/socket.io.js"></script>
  </body>
</html>

Next copy the following to your static/styles.css file. With these styles we are creating a very simple toast notification container. Credit where credit is due, the styles and the animation are both heavily influenced by W3 Schools Snackbar/Toast.

body {
  margin: 0;
  padding-bottom: 3rem;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.toast {
  visibility: hidden;
  min-width: 250px;
  margin-left: -125px;
  background-color: #333;
  color: #fff;
  text-align: center;
  border-radius: 2px;
  padding: 16px;
  position: fixed;
  z-index: 1;
  left: 50%;
  top: 30px;
  font-size: 17px;
  box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,.1);
}

.toast.show {
  visibility: visible;
  -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
  animation: fadein 0.5s, fadeout 0.5s 2.5s;
}

#socketid {
  padding-top: 10px;
}

#update {
  background-color: #29bd86;
  border: none;
  color: white;
  padding: 10px 24px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  cursor: pointer;
}

#container {
  margin-left: 20px;
}

@-webkit-keyframes fadein {
  from {top: 0; opacity: 0;} 
  to {top: 30px; opacity: 1;}
}

@keyframes fadein {
  from {top: 0; opacity: 0;}
  to {top: 30px; opacity: 1;}
}

@-webkit-keyframes fadeout {
  from {top: 30px; opacity: 1;} 
  to {top: 0; opacity: 0;}
}

@keyframes fadeout {
  from {top: 30px; opacity: 1;}
  to {top: 0; opacity: 0;}
}

Finally, inside index.js let’s scaffold our Express application, import Socket.io and serve our index.html file on port 3000:

const express = require('express')
const app = express()
const http = require('http').Server(app)
const io = require('socket.io')(http)
const port = process.env.PORT || 3000

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html')
})

app.use('/static', express.static('static'))

As a quick recap, we just created our index.html page which will act as our client for the Socket.io implementation. We then copied some files into styles.css which is included inside of index.html. After that we set up index.js which will provide the server implementation of Socket.io.

By now your directory structure should resemble:

project/
|   index.js    
|   index.html    
|   static/
|   |   styles.css  
|   node_modules/    
|   package.json    

At this time you can run the application locally using nodemon. Doing so will automatically restart the server any time you make changes to index.js:

nodemon index.js

Creating A Global Toast

Creating a global event in Socket.io is very simple. Let’s start out by editing our index.js and creating a listener for the global notification event.

For the sake of example, let’s say we have a background job that syncs user account details across systems every 6 seconds. To mimic this, we’re going to set up an event that will be emitted every 6 seconds called global sync:

io.on('connection', socket => {
  // Global toast
  setInterval(() => { io.emit('global sync', 'Users have been synced') }, 6000)
})

In the code above, we’re tapping into the connection event for Socket.io and via setInterval we’re repeatedly emitting a “global sync” message to all open connections. Now that the server side of the application is emitting the event, we’re going to need to configure our client, index.html, to listen for the event being emitted and take some action. In this case, we’re going to add a listener for global sync and upon receiving the message we will make the globalToast div visible and set it’s contents to the message we received from the server:

/* just below socket.io.js inclusion */
<script>
     var socket = io();

    function animateToast(id, msg) {
      var item = document.getElementById(id);

      item.innerHTML = msg;
      item.className = "toast show";
      setTimeout(function() { item.className = item.className.replace("show", ""); }, 3000);
    }

    socket.on('global sync', function(msg) {
      animateToast('globalToast', msg);
    });
</script>

At this point, if you refresh your browser you will be able to see the global toast notification appearing every 6 seconds near the top of your screen. While this is a fairly contrived example, it does provide a good basis for creating global user notifications using Socket.io.

Creating a User Specific Toast

We’ve seen how to send a toast to all of our users, but how do we specifically target a single user? To do that we need to capture the socket id when the connection is first established and then refer to just that socket when emitting our message.

For this example we’ll first rig up our update settings button in index.html to emit a message to the server:

/* just below prior socket.on */
document.getElementById("update").addEventListener("click", function() {
  socket.emit('user update', { active: true })
});

Then in index.js we’ll set up the listener which will wait for 2 seconds and emit the user update event, letting the user know that their changes have been saved. You’ll notice that here we’ve attached the

socket.on('user update', (msg) => {
  setTimeout(() => {
    io.to(socket.id).emit('user update', `Settings Updated for ${socket.id}`)
  }, 2000)
})

Now, we’ll head back into index.html and add the final listener, this time in the client, so that when the user update responds after the 2 second delay we will see our toast notification:

socket.on('user update', function(msg) {
  animateToast('userToast', msg);
});

Once your changes have been made be sure to refresh your browser for the most recent changes to take effect. Go ahead and click the Update Settings button and after 2 seconds you should receive a toast letting you know that the update was successful.

Now you may be wondering, how can I be sure that these messages are unique to each user outside of the socket id? Well, the simplest way is to open a second tab in your browser and navigate to localhost on port 3000. From there go ahead and press the Update Settings button and you will see that the events are emitted and responded to independent of one another while the global event still takes place for all users.

Conclusion and Source Code

While these are two relatively contrived and simple examples of using Socket.io for real time toast notifications, hopefully they will inspire you to leverage this awesome technology to create more interactive websites and applications.

You can download the final copy of the code from here: https://github.com/BenDurham/creating-interactive-websites-with-socketio. If you have any questions or run into issues let me know in the comments below. If you would like to see more of these types of tutorials, likewise, feel free to reach out.