YouTube Courses - Learn Smarter

YouTube-Courses.site transforms YouTube videos and playlists into structured courses, making learning more efficient and organized.

WebSockets Tutorial (Node & Socket.io Chat App)

Build real-time applications with this WebSockets tutorial series! Learn how to use Node.js and Socket.io to create a live chat app with instant message updates. Perfect for developers looking to add real-time communication to their web projects.



Introduction to WebSockets: Real-Time Communication for the Web

This chapter introduces the concept of WebSockets and their application in modern web development. We will explore how WebSockets enable real-time, bi-directional communication between clients and servers, revolutionizing web applications that require instant data updates. Through real-world examples and a practical tutorial project, you will gain a solid understanding of WebSockets and their potential.

What are WebSockets?

WebSockets are a communication protocol that facilitates persistent, two-way communication channels between a client (like a web browser) and a server. Unlike traditional HTTP request-response cycles, WebSockets maintain an open connection, allowing for immediate data exchange in both directions.

WebSocket: A communication protocol that provides full-duplex communication channels over a single TCP connection. It enables real-time data transfer between a client and a server.

This continuous connection is the key difference between WebSockets and older technologies like AJAX, which rely on clients repeatedly requesting updates from the server.

AJAX (Asynchronous JavaScript and XML): A set of web development techniques using many web technologies on the client-side to create asynchronous web applications. In traditional AJAX, the client must initiate requests to the server to check for updates.

Bi-Directional and Real-Time Communication

The defining feature of WebSockets is their bi-directional nature.

Bi-directional Communication: A communication system where data can flow in both directions simultaneously between two endpoints, such as a client and a server.

This means:

  • Client to Server Communication: Clients can send data to the server.
  • Server to Client Communication: Servers can push data to clients without waiting for a request.

Because these connections remain constantly open, WebSockets enable real-time data flow.

Real-time Data Flow: The immediate and continuous transmission of data as it is generated or becomes available, allowing for instantaneous updates and interactions.

This capability is crucial for applications where timely information delivery is paramount.

Real-World Applications of WebSockets: The Chat Application Example

To understand the practical implications of WebSockets, consider a common example: a chat application. Imagine multiple users accessing a chat application hosted on a server from different locations around the world using their web browsers.

When a user, for instance, “John,” types a message and clicks “send,” the following process occurs using WebSockets:

  1. Establishing a WebSocket Connection: When each user initially accesses the chat application in their browser, a WebSocket connection is established between their browser (the client) and the server. This connection remains open throughout the session.
  2. Message Transmission: When John sends a message, the message is transmitted through his established WebSocket connection to the server.
  3. Server-Side Processing and Broadcast: The server receives the message and identifies it as a chat message. It then uses its connections to all other connected clients (browsers of other users) to send the message down each of their respective WebSocket connections.
  4. Instantaneous Update: As a result, all other users viewing the chat application instantly see John’s message appear in their chat window without their browsers having to actively request updates from the server.

This entire data transfer process happens in real-time, eliminating the need for each client to periodically send requests to the server to check for new messages. This is a fundamental shift from traditional web communication methods and highlights the efficiency and responsiveness of WebSockets.

Beyond Chat: Diverse Applications of WebSockets

The utility of WebSockets extends far beyond chat applications. Their real-time capabilities make them ideal for a wide range of applications, including:

  • Multiplayer Browser Games: Enabling real-time interactions and updates between players in online games.
  • Collaborative Code Editing Software: Allowing multiple developers to edit code simultaneously and see changes in real-time.
  • Live Text Updates for Sports and News Websites: Providing instant scores, news updates, and commentary.
  • Online Drawing Canvases: Facilitating collaborative drawing and real-time sharing of artwork.
  • Real-Time To-Do Apps: Synchronizing task lists and updates instantly across multiple devices.

These examples illustrate the versatility of WebSockets and their growing importance in creating interactive and dynamic web experiences.

Tutorial Project: Building a Simple Chat Application

In this tutorial series, we will build a simple chat application to demonstrate the practical implementation of WebSockets. This application will feature:

  • Real-time messaging: Users can send and receive messages instantly.
  • User handles: Users can set a name or handle to identify themselves in the chat.
  • ”Typing” indicators: Real-time feedback to show when another user is currently typing a message.

We will simulate a scenario with two browser windows connected to the same server, representing different users in different locations. This setup will allow us to observe the real-time communication facilitated by WebSockets in action.

The application will be named “Muriel Chat,” featuring a Mario theme, demonstrating the practical application of the concepts discussed.

Tools Required for Development

To follow along with the tutorial, you will need to install and set up the following tools:

  • Adam: A free and customizable text editor for writing code. You can download it from [ndu.io](ndu.io - Note: This URL might be a typo and should be checked. A popular alternative text editor name is Atom, which can be found at atom.io).

  • CMD or Commander (Cmder): A command-line tool for executing commands and managing your development environment. Cmder is a popular enhanced console emulator for Windows and is also free.

  • Node.js: A JavaScript runtime environment that allows you to run JavaScript code outside of a web browser, essential for server-side development in this tutorial.

    Node.js: An open-source, cross-platform JavaScript runtime environment that executes JavaScript code server-side. It is built on Chrome’s V8 JavaScript engine.

    To check if Node.js is installed, open your command-line tool and type node -v. If Node.js is installed, it will display the version number. You can download Node.js from nodejs.org.

  • Course Files (GitHub Repository): All the code files for this tutorial series are available on a GitHub repository called “WebSockets playlist.”

    GitHub Repository: A storage location for software projects using Git version control. It allows for collaborative development, tracking changes, and sharing code.

    Each lesson in the tutorial corresponds to a different branch within the repository.

    Branch (in Git): A parallel version of a repository, allowing developers to work on new features or fixes without affecting the main codebase.

    You can access and download the code for each lesson by selecting the appropriate branch from the repository. The link to the GitHub repository will be provided.

Conclusion

This chapter has provided an introduction to WebSockets, highlighting their fundamental principles and diverse applications. Understanding the concepts of bi-directional and real-time communication is crucial for leveraging WebSockets effectively in modern web development. The upcoming tutorial series will offer hands-on experience in building a chat application, solidifying your understanding and practical skills in using WebSockets. We encourage you to explore the provided resources and prepare your development environment to embark on this exciting journey into real-time web communication.


Introduction to WebSockets with Node.js and Express

This chapter will guide you through the initial setup of a Node.js server using Express, preparing the groundwork for implementing WebSocket communication in subsequent chapters. We will focus on creating a basic Express application capable of serving static files, which is a fundamental step before integrating WebSocket functionality. This is not intended to be an exhaustive guide to Node.js or Express, but rather a focused tutorial on the essentials needed for WebSocket implementation. For a more in-depth exploration of Node.js and Express, please refer to dedicated resources.

Setting up the Project Environment

Before diving into the code, we need to set up our project environment. This involves initializing a Node.js project and installing necessary dependencies.

Project Initialization

  1. Create a Project Folder: Begin by creating a new folder for your project. In this example, we are using a folder named “websockets playlist”.

  2. Initialize Node.js Project: Navigate to your project folder in the command line or terminal.

  3. Run npm init: Execute the command npm init. This command initializes a new Node.js project and guides you through creating a package.json file.

    npm init: This command is used in Node.js to initialize a new project. It creates a package.json file, which is essential for managing project dependencies and metadata.

  4. Answer Project Questions: npm init will prompt you with a series of questions about your project, such as the project name, version, description, entry point, and author. You can accept the default values or customize them as needed. For this tutorial, default values for name and version are acceptable. The entry point should be set to index.js, which we will create shortly.

    After answering the questions, npm init will generate a package.json file in your project directory. This file will contain metadata about your project and, importantly, will track your project’s dependencies.

Installing Dependencies

To build our Express application and facilitate server development, we need to install two key packages using npm (Node Package Manager).

  1. Install Express: Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. To install Express, run the following command in your terminal within the project directory:

    npm install express --save

    Express: Express.js is a popular Node.js web application framework that provides a wide array of features for building web applications and APIs. It simplifies the process of creating server-side applications with Node.js.

    The --save flag adds Express as a dependency in your package.json file under the dependencies section. This indicates that Express is required for your application to run. Upon successful installation, you will observe two changes:

    • A dependencies section is added to your package.json file, listing “express” and its version.

    • A node_modules folder is created in your project directory. This folder contains all the installed packages and their dependencies. You generally do not need to modify the contents of this folder directly.

    dependencies: In the context of package.json, dependencies are packages that your project needs to run in a production environment. These are libraries or modules that your code relies on to function correctly.

    node_modules: This directory in a Node.js project houses all the installed npm packages and their dependencies. It’s automatically created when you install packages using npm.

  2. Install Nodemon (as a Dev Dependency): Nodemon is a utility that monitors for any changes in your Node.js application and automatically restarts the server. This is extremely useful during development as it saves you from manually restarting the server every time you make code changes. To install nodemon, use the following command:

    npm install nodemon --save-dev

    nodemon: Nodemon is a utility that automatically restarts your Node.js server when file changes in the directory are detected. It is primarily used during development to enhance productivity by eliminating the need for manual server restarts.

    The --save-dev flag adds nodemon as a devDependency in your package.json file. devDependencies are packages that are only needed for development and testing, not for the application to run in a production environment. After installation, you will see “nodemon” listed under the devDependencies section in your package.json file.

    Dev dependencies: In package.json, devDependencies are packages that are required for development tasks such as testing, linting, and in this case, automatic server restarting with nodemon. They are not necessary for the application to run in production.

Creating the Express Application

With the project environment set up and dependencies installed, we can now create our Express application.

Setting up index.js

  1. Create index.js: Create a new file named index.js in your project’s root directory. This file will serve as the entry point for our Node.js application.

    index.js: This is a common convention for the main entry point file in a Node.js application. When you start your Node.js application, Node.js will typically look for and execute index.js by default if no other entry point is specified.

  2. Require Express: At the beginning of your index.js file, you need to import the Express module using the require() function. This makes the functionalities of Express available in your application.

    const express = require('express');

    require(): In Node.js, require() is a function used to import modules or libraries into your current file. It’s how you access and use external code in your Node.js applications.

  3. Initialize Express App: Next, create an instance of an Express application by calling the express() function and store it in a variable, typically named app.

    const app = express(); // App setup

    app setup: This refers to the process of initializing and configuring an Express application instance, making it ready to handle requests and define routes.

  4. Create an HTTP Server: To start the server and make it listen for incoming requests, use the app.listen() method. This method takes a port number as an argument and optionally a callback function that executes once the server starts listening.

    const server = app.listen(4000, () => { // Creating a server
      console.log('Listening to requests on port 4000');
    });

    app.listen(): This is an Express method that starts the HTTP server on the specified port and begins listening for incoming connections. It’s essential for making your web application accessible.

    callback function: A callback function is a function passed as an argument to another function. It is executed after the outer function completes its operation. In this context, the callback function in app.listen() is executed once the server has started listening on the specified port.

    In this example, the server is set to listen on port 4000. The callback function logs a message to the console confirming that the server is running.

  5. Run the Server: To start your server, execute the following command in your terminal:

    nodemon index.js

    Using nodemon instead of node index.js will automatically restart the server whenever you save changes to your index.js file or any other files being watched by nodemon.

Serving Static Files

Currently, if you navigate to http://localhost:4000 in your browser, you will receive a “Cannot GET /” message. This is because we haven’t yet defined how the server should handle GET requests to the root path (”/”). To serve static files, such as HTML, CSS, and JavaScript files, we need to use middleware.

  1. Create a public Folder: In your project’s root directory, create a new folder named public. This folder will contain all the static files that we want to serve to the browser.

    public folder: A commonly used directory in web applications to store static assets like HTML files, CSS stylesheets, JavaScript files, images, and other resources that are directly served to the client-side (browser).

  2. Add index.html: Inside the public folder, create a new file named index.html. This will be the main HTML file served when a user accesses your application. Add basic HTML content to this file, for example:

    <!DOCTYPE html>
    <html>
    <head>
        <title>WebSockets 101</title>
    </head>
    <body>
        <h1>Woo you see me</h1>
    </body>
    </html>

    index.html: By convention, index.html is often used as the default file name for the main HTML document in a directory. When a user requests a directory from a web server without specifying a file name, the server will typically look for and serve index.html if it exists in that directory.

  3. Serve Static Files with Middleware: In your index.js file, add the following middleware to serve static files from the public folder:

    app.use(express.static('public')); // Static files middleware

    middleware: In Express.js, middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. They can perform various tasks such as logging, authentication, and serving static files.

    static files: These are files that are served to users exactly as they are stored on the server, such as HTML files, CSS stylesheets, JavaScript files, images, and fonts. They are contrasted with dynamic content which is generated by the server in response to user requests.

    express.static: This is a built-in middleware function in Express.js that serves static files from a specified directory. In this case, it’s configured to serve files from the ‘public’ directory.

    app.use() is used to mount middleware functions at a specific path. express.static('public') is the middleware that serves static files from the public directory. Now, when you access http://localhost:4000 in your browser, Express will automatically look for and serve index.html from the public folder.

  4. Add styles.css (Optional): To enhance the visual presentation, you can create a styles.css file inside the public folder and link it to your index.html. Add some CSS rules to styles.css and link it in the <head> section of your index.html file:

    <!DOCTYPE html>
    <html>
    <head>
        <title>WebSockets 101</title>
        <link rel="stylesheet" href="/styles.css">
    </head>
    <body>
        <h1>Woo you see me</h1>
    </body>
    </html>

    link tag: In HTML, the <link> tag is used to define the relationship between the current document and an external resource. It is most commonly used to link external stylesheets to HTML documents.

    href: The href attribute in the <link> tag specifies the URL of the external resource being linked, such as a stylesheet.

    rel: The rel attribute in the <link> tag specifies the relationship between the current document and the linked resource. For stylesheets, it’s typically set to “stylesheet”.

    stylesheet: A stylesheet is a file that describes the presentation of a document written in a markup language like HTML. CSS (Cascading Style Sheets) is the language commonly used to define stylesheets for web pages.

    Ensure that styles.css is located in the public folder so that Express can serve it correctly.

Now, when you refresh http://localhost:4000 in your browser, you should see the content of your index.html file, styled by styles.css if you added it.

Conclusion

In this chapter, we have successfully set up a basic Express application using Node.js. We initialized a Node.js project, installed necessary dependencies (Express and nodemon), created an index.js file to set up the Express server, and configured it to serve static files from the public folder. This provides a solid foundation for the next steps, where we will integrate WebSocket functionality into this application to enable real-time communication between the server and clients.


Introduction to WebSockets and Socket.IO

This chapter introduces the concept of WebSockets and explores how to use the Socket.IO library to implement real-time, bidirectional communication between a server and a web browser. We will build upon a basic Node.js and Express application to set up WebSocket connections using Socket.IO, laying the foundation for building interactive web applications.

Setting Up the Server-Side with Socket.IO

In this section, we will configure our server, built with Node.js and Express, to handle WebSocket connections using Socket.IO.

Installing Socket.IO on the Server

To begin, we need to install the Socket.IO library as a dependency for our Node.js server. This is done using npm, the Node Package Manager. Open your command line tool, navigate to your project directory (where your index.js file is located), and execute the following command:

npm install socket.io --save

npm (Node Package Manager) A package manager for JavaScript, used to install and manage project dependencies. It comes bundled with Node.js.

Dependencies External libraries or modules that a project relies on to function correctly. The --save flag in npm install adds the installed package to your project’s package.json file, listing it as a dependency.

This command downloads and installs the Socket.IO library and adds it to your project’s dependencies.

Requiring and Initializing Socket.IO in index.js

Once installed, Socket.IO needs to be included and initialized in our server-side JavaScript file (index.js). We use the require() function in Node.js to import the Socket.IO module.

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const socketIO = require('socket.io');
const io = socketIO(server);

// ... rest of your server code ...

require() In Node.js, require() is a function used to import modules or libraries into your current file. It allows you to use code defined in other files or packages within your application.

In the code snippet above, we first require the socket.io module and store it in the socketIO constant. Then, we initialize Socket.IO by calling the socketIO() function and passing our server instance as an argument. This binds Socket.IO to our existing HTTP server, enabling it to handle WebSocket connections. The returned object from socketIO(server) is stored in the io constant. This io object is central to managing server-side Socket.IO functionality.

Setting up Socket.IO Connection Handling

Now that Socket.IO is initialized on the server, we need to listen for incoming WebSocket connections from clients. Socket.IO provides the on('connection') method on the io object to handle new connections.

// Socket setup
io.on('connection', (socket) => {
  console.log('Made socket connection');
  console.log('Socket ID:', socket.id);
});

Callback Function A function passed as an argument to another function, which is executed after the outer function completes its operation or when a specific event occurs. In this case, the callback function is executed when a client establishes a WebSocket connection.

Event In programming, an event is an action or occurrence that happens in a system that the system can react to. The 'connection' event in Socket.IO is triggered when a new client successfully connects to the server via WebSocket.

Socket (in the context of WebSockets) A bidirectional communication channel between a client and a server over a network. In Socket.IO, the socket object represents a single connection between a client and the server, allowing for real-time data exchange.

The io.on('connection', ...) line sets up an event listener. It listens for the connection event, which is emitted by Socket.IO whenever a client successfully establishes a WebSocket connection with the server. When a connection occurs, the provided callback function is executed.

This callback function receives a socket object as an argument. This socket object represents the individual WebSocket connection between the server and a specific client. Inside the callback, we are currently logging a message to the server’s console indicating a successful connection and also logging the unique socket.id. Each new client connection will result in a unique socket.id.

Setting Up the Client-Side with Socket.IO

To establish a WebSocket connection from the browser (client-side), we also need to include the Socket.IO library in our HTML file and write JavaScript code to initiate the connection.

Including the Socket.IO Client Library in index.html

The Socket.IO client library needs to be included in our index.html file. You can obtain the client library from the official Socket.IO website or a CDN (Content Delivery Network). In this example, we’ll assume you are using the version provided by the Socket.IO website. You would typically download this file and serve it from your public directory or link to a CDN. For simplicity, the transcript uses a direct link to the Socket.IO server.

Add the following <script> tag within the <head> or before the closing </body> tag of your index.html file, ensuring it is placed before any custom JavaScript files that will use Socket.IO:

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Example</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <h1>Welcome to WebSockets!</h1>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
    <script src="/chat.js"></script>
</body>
</html>

Library (in programming) A collection of pre-written code, functions, and routines that can be used by programs to perform specific tasks. Libraries simplify development by providing reusable components, saving developers from writing code from scratch for common functionalities.

This line includes the Socket.IO client library in our HTML page. This makes the io object available in the browser’s JavaScript environment.

Creating chat.js for Client-Side Socket.IO Logic

Next, create a new JavaScript file named chat.js within your public directory. This file will contain the client-side JavaScript code to establish the WebSocket connection. In chat.js, add the following code:

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

Client-side / Front-end Refers to the part of a web application that runs in the user’s web browser. It is responsible for the user interface and user interaction.

Server-side / Back-end Refers to the part of a web application that runs on the server. It handles data storage, business logic, and communication with the client-side.

localhost A hostname that refers to the current computer being used. It is commonly used for accessing services running on the same machine, such as a web server during development. The IP address associated with localhost is typically 127.0.0.1.

Port (Networking) A virtual point where network connections start and end. Ports allow multiple applications or services to run on the same computer and differentiate their network traffic. Port 4000 in this context is the port number on which our Node.js server is listening for connections.

The io.connect('http://localhost:4000') line in chat.js initiates a connection to the Socket.IO server running at http://localhost:4000. The io object is available because we included the Socket.IO client library in index.html. This line establishes a WebSocket connection to the server. The returned value is assigned to the socket constant, which represents the client-side socket object. This socket object will be used to send and receive data from the server.

Testing the WebSocket Connection

To test if the WebSocket connection is successfully established, ensure your Node.js server (index.js) is running. In your command line, in the project directory, execute:

nodemon index.js

nodemon A utility that monitors for changes in your Node.js application and automatically restarts the server. This is helpful during development as it saves you from manually restarting the server every time you make a code change. node is the standard command to run a Node.js application, but nodemon enhances the development workflow.

Then, open index.html in your web browser. If the setup is correct, you should see the message “Made socket connection” and the “Socket ID:” followed by a unique identifier logged in your server’s console. Each time you refresh the index.html page in the browser, a new WebSocket connection will be established, and you will see a new “Socket ID” in the server console, demonstrating that a new socket connection is being created for each client.

Conclusion

In this chapter, we successfully set up a basic WebSocket connection between a server and a client using Socket.IO. We installed Socket.IO on the server, initialized it, and handled the ‘connection’ event. On the client-side, we included the Socket.IO client library and established a connection to the server. This provides the foundation for building real-time applications. In the subsequent chapters, we will explore how to send and receive data over these WebSocket connections to create interactive features like a chat application.


Building a Real-Time Chat Application with WebSockets and Socket.IO: A Step-by-Step Guide

This chapter guides you through the initial steps of building a real-time chat application using WebSockets and Socket.IO. We will focus on setting up the basic structure, establishing communication between the client and server, and implementing the functionality to send and receive chat messages.

1. Introduction to Real-Time Communication with WebSockets

In modern web applications, real-time communication is crucial for features like chat applications, live notifications, and collaborative tools. WebSockets provide a persistent, bi-directional communication channel between a client (like a web browser) and a server. This allows for instant data transfer in both directions without the need for constant requests from the client.

WebSocket: A communication protocol that provides full-duplex communication channels over a single TCP connection. It enables real-time data exchange between a client and a server.

In this tutorial, we will be using Socket.IO, a library that simplifies the use of WebSockets and provides fallback mechanisms for browsers that do not fully support WebSockets.

Socket.IO: A JavaScript library for real-time web applications. It enables bidirectional communication between web clients and servers, using WebSockets when possible and falling back to other techniques when necessary.

2. Setting Up Socket.IO on the Client and Server

In the previous steps (covered in earlier materials), we have already set up Socket.IO on both the client-side (front-end) and server-side (back-end) of our application. This setup allows us to establish a persistent connection between the browser and the server.

When a client (browser) requests the index page from the server, the server responds, and a WebSocket connection is established. This connection allows us to transfer data between the client and the server in real-time.

3. Structuring the Chat Application’s Front-End (HTML)

Let’s start building the visual structure of our chat application by modifying the index.html file. We will replace any existing placeholder content with the HTML elements needed for our chat interface.

3.1. Creating the Main Chat Container

We will begin by creating a main container div to encompass the entire chat application. This container will have an ID of mario-chat.

<div id="mario-chat">
    </div>

3.2. Building the Chat Window

Inside the main container, we need a div to serve as the chat window, where messages will be displayed. This will have an ID of chat-window. Within the chat window, we will have another div with the ID output. This output div will be specifically used to display the incoming chat messages.

<div id="mario-chat">
    <div id="chat-window">
        <div id="output"></div>
    </div>
</div>

3.3. Adding Input Fields for User Interaction

Below the chat window, we will add input fields for users to enter their handle (username) and their message.

  • Handle Input: We create an input element with the ID handle. This will be a text input field with a placeholder “Handle” to guide the user.

    <input type="text" id="handle" placeholder="Handle" />
  • Message Input: Similarly, we create another input element with the ID message. This will also be a text input field, with a placeholder “Message” for user input.

    <input type="text" id="message" placeholder="Message" />

3.4. Implementing the Send Button

Finally, we need a button to trigger the sending of the message. We will create a button element with the ID send and the text “Send”.

<button id="send">Send</button>

Putting it all together, our index.html structure for the chat application looks like this within the body tags:

<div id="mario-chat">
    <div id="chat-window">
        <div id="output"></div>
    </div>
    <input type="text" id="handle" placeholder="Handle" />
    <input type="text" id="message" placeholder="Message" />
    <button id="send">Send</button>
</div>

At this stage, with pre-existing CSS styles applied (as mentioned in the transcript), the basic visual layout of the chat application will be visible in the browser. However, no functionality is yet implemented for sending or receiving messages.

4. Client-Side JavaScript: Sending Chat Messages

Now we will implement the JavaScript logic in chat.js (client-side JavaScript file) to handle user input and emit messages to the server via Socket.IO.

4.1. Querying the Document Object Model (DOM)

The first step is to access and store references to the HTML elements we created in index.html. This is done using the Document Object Model (DOM), which represents the structure of the HTML document as a tree of objects.

Document Object Model (DOM): A programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as a tree of objects.

We will use document.getElementById() to select the handle, message, send button, and output elements and store them in JavaScript variables.

// Query DOM
var message = document.getElementById('message'),
    handle  = document.getElementById('handle'),
    btn     = document.getElementById('send'),
    output  = document.getElementById('output');

4.2. Emitting Events: Sending Messages to the Server

We want to send a message to the server when the user clicks the “Send” button. To achieve this, we will attach an event listener to the send button.

Event Listener: A procedure or function in JavaScript that waits for a specific event to occur (like a click, key press, etc.) and then executes a predefined function in response.

We use the addEventListener() method to listen for a click event on the btn (send button) element. When a click occurs, we will execute a callback function.

Callback Function: A function passed as an argument to another function, to be executed at a later time. In event listeners, the callback function is executed when the specified event occurs.

Inside the callback function, we will use our socket object (established during Socket.IO setup) to emit a custom event named chat.

Emit (in Socket.IO context): To send a message or event from one part of the application (client or server) to another part (server or client) via the established WebSocket connection.

The emit() function takes two arguments:

  1. Event Name: A string that identifies the type of message being sent. In our case, it’s 'chat'.
  2. Data Payload: The data we want to send with the message. Here, we are sending an object containing the message and handle values from the input fields. We access the values of the input fields using .value.
// Emit events
btn.addEventListener('click', function(){
    socket.emit('chat', {
        message: message.value,
        handle: handle.value
    });
});

This code snippet ensures that when the “Send” button is clicked, a chat message event, along with the user’s handle and message, is emitted to the server through the WebSocket connection.

5. Server-Side JavaScript: Handling and Broadcasting Messages

Now, we need to handle the incoming chat message event on the server-side (in index.js) and then broadcast it to all connected clients, so every user in the chat room can see the message.

5.1. Listening for ‘chat’ Events on the Server

Inside the connection callback function in index.js (which is executed when a new client connects), we will use socket.on() to listen for the chat event emitted from the client.

On (in Socket.IO context): To set up a listener on a socket to receive and handle specific events or messages emitted from the other end of the connection (client or server).

When the server receives a chat event, the provided callback function is executed. This callback function receives the data payload sent from the client, which in our case is the object containing message and handle.

socket.on('chat', function(data){
    // ... handle chat message ...
});

5.2. Broadcasting Messages to All Clients

Inside the chat event handler on the server, we want to send the received message to all connected clients. We can achieve this using io.sockets.emit(). io.sockets refers to all currently connected sockets.

By using io.sockets.emit(), we are emitting a chat event to every connected socket, including the socket that originally sent the message. This ensures that all clients receive the message. We are also sending the same data object that we received from the original client, so all clients receive the handle and message.

socket.on('chat', function(data){
    io.sockets.emit('chat', data);
});

This server-side code effectively receives a chat message from one client and then broadcasts that message to all connected clients, enabling real-time message sharing.

6. Client-Side JavaScript: Receiving and Displaying Messages

Finally, we need to handle the incoming chat event on the client-side and display the messages in the chat window (output div).

6.1. Listening for ‘chat’ Events on the Client

In chat.js, we again use socket.on() to listen for the chat event, but this time, we are listening for events from the server.

// Listen for events
socket.on('chat', function(data){
    // ... handle incoming chat message ...
});

6.2. Outputting Messages to the DOM

Inside the chat event handler on the client, we receive the data object (containing handle and message) that was broadcasted from the server. We need to dynamically update the content of the output div to display this new message.

We achieve this by manipulating the innerHTML property of the output element.

innerHTML: A property in JavaScript that allows you to get or set the HTML content within an element.

We use output.innerHTML += to append new HTML content to the existing content of the output div. For each incoming message, we create a new paragraph (<p>) element. Inside the paragraph, we format the message to display the handle in bold (<strong>) followed by the message text.

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

This client-side code listens for chat events from the server. When a message is received, it dynamically creates HTML to display the sender’s handle and the message within the output div, effectively updating the chat window in real-time for all connected clients.

7. Testing the Basic Chat Application

With the HTML structure, client-side JavaScript for sending and receiving messages, and server-side JavaScript for handling and broadcasting, we now have a basic working chat application.

By opening the index.html in multiple browser tabs or on different computers connected to the same server, you can test the real-time chat functionality. Entering a handle and message in one browser and clicking “Send” will broadcast that message to all other open browsers, demonstrating the basic functionality of a real-time chat application built with WebSockets and Socket.IO.

This chapter has covered the foundational steps in building a real-time chat application. Further chapters can expand upon this foundation by adding features like user presence indicators (“is typing”), private messaging, and more robust styling and user interface enhancements.


WebSocket Broadcasting: Enhancing Real-time Communication

Introduction to WebSocket Broadcasting

This chapter delves into the concept of broadcasting messages using WebSockets. Building upon previous WebSocket tutorials, we will explore how broadcasting differs from direct message sending and how it can be effectively utilized in real-time applications. Specifically, we will focus on implementing a “typing indicator” feature in a chat application to illustrate the power and utility of WebSocket broadcasting.

Understanding Broadcasting: A Key Difference

In prior examples, when a client sent a message to the server, the server would process it and then emit a message back to every connected client, including the sender. This approach, while functional, is not always efficient or desirable for certain features.

Broadcasting, in the context of WebSockets, offers a distinct approach.

Broadcasting: In WebSocket communication, broadcasting refers to the server sending a message to all connected clients except the client that initiated the original message or event. This selective message distribution is crucial for features where information is relevant to all users except the originator.

With broadcasting, if a client sends a message to the server, and the server then broadcasts a response, that response will be sent to all connected clients excluding the original sender. This nuanced behavior is essential for creating a more refined and user-friendly real-time experience.

Illustrative Example: The “Typing” Indicator

Consider a chat application where you want to display a “User is typing…” message to other users when someone is actively composing a message. Broadcasting is perfectly suited for this scenario. If a user is typing in their chat window, it’s unnecessary for their own window to display “User is typing…“. Instead, this message is only relevant and should be displayed in the chat windows of other connected users. This is precisely what WebSocket broadcasting enables.

Implementing Broadcasting: Front-End Changes

To implement the typing indicator feature using broadcasting, we need to modify both the front-end (client-side) and back-end (server-side) code. Let’s begin with the necessary adjustments to the index.html and chat.js files, which constitute our client-side application.

Adding the feedback Div in index.html

First, we need to create a designated area in our index.html file to display the “typing…” messages. We will add a new div element beneath the existing output div. This new div will have the ID feedback.

<div id="output"></div>
<div id="feedback"></div>

This feedback div will serve as the container where the “typing…” messages will be dynamically injected via JavaScript.

Selecting the feedback Element in chat.js

Next, we need to access this newly created feedback div within our chat.js file. This allows us to manipulate its content using JavaScript. We can achieve this by using document.getElementById() and storing the reference in a JavaScript variable, similar to how we accessed other HTML elements previously.

// ... existing code ...
feedback = document.getElementById('feedback');

This line of code retrieves the HTML element with the ID feedback and assigns it to the feedback variable in our JavaScript code, making it accessible for further manipulation.

Implementing the Keypress Event Listener for Typing

To detect when a user is typing, we will attach an event listener to the message input field.

Event Listener: In JavaScript, an event listener is a function that waits for a specific event to occur on an HTML element. When the event happens, the function (callback function) is executed, allowing you to respond to user interactions or browser events.

Specifically, we will listen for the keypress event on the message input field.

Keypress Event: The keypress event is triggered in JavaScript when a key is pressed down and then released on the keyboard while an element (like an input field) has focus. This event allows us to detect individual keystrokes as a user types.

When a keypress event occurs within the message input field, we want to emit a message to the server indicating that the user is typing. We will use socket.emit() for this purpose.

Emit (in WebSockets): In WebSockets, to emit means to send a message from one WebSocket connection to another. socket.emit() is a function used to send custom events and data from the client to the server, or vice-versa, over the established WebSocket connection.

Here’s the JavaScript code to add the keypress event listener and emit a ‘typing’ message to the server:

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

This code snippet does the following:

  • message.addEventListener('keypress', ...): Attaches an event listener to the message input field, listening for the keypress event.
  • function () { ... }: Defines a callback function that will be executed every time a keypress event occurs in the message input.
  • socket.emit('typing', handle.value);: Inside the callback function, we use socket.emit() to send a message to the server.
    • 'typing': This is the custom event name we are defining. It signals to the server that a user is typing.
    • handle.value: This is the data we are sending along with the ‘typing’ event. Here, we are sending the user’s handle (username) so that other clients can display who is typing. We retrieve the handle value from the handle input field.

Implementing Broadcasting: Server-Side Logic (index.js)

Now, let’s move to the server-side code in index.js to handle the ‘typing’ event and implement the broadcasting logic.

Handling the ‘typing’ Event and Broadcasting

In our index.js file, within the io.on('connection', ...) function, we need to listen for the ‘typing’ event emitted by the client. We use socket.on() for this.

Socket.on(): socket.on() is a function used in both client-side and server-side WebSocket code to set up an event listener for custom events received over a WebSocket connection. It allows the server and client to react to specific messages sent by each other.

When the server receives a ‘typing’ event, it should then broadcast this event to all other connected clients except the sender. We use socket.broadcast.emit() to achieve this.

Socket.broadcast.emit(): socket.broadcast.emit() is a server-side function in Socket.IO that allows the server to send a message to all connected clients except for the client that initiated the event or message. This is the core function for implementing broadcasting in Socket.IO.

Here is the server-side code to handle the ‘typing’ event and broadcast it:

io.on('connection', (socket) => {
    // ... existing code ...

    socket.on('typing', (data) => {
        socket.broadcast.emit('typing', data);
    });

    // ... existing code ...
});

This code snippet:

  • socket.on('typing', ...): Sets up an event listener on the server-side socket object, listening for the ‘typing’ event.
  • (data) => { ... }: Defines a callback function that is executed when the ‘typing’ event is received from a client. The data parameter will contain the data sent by the client (in our case, the user’s handle).
  • socket.broadcast.emit('typing', data);: Inside the callback, socket.broadcast.emit() is used to broadcast the ‘typing’ event.
    • 'typing': This is the event name being broadcasted.
    • data: This is the data being broadcasted along with the event (the user’s handle). This data will be received by all other connected clients.

Handling Broadcasted Messages: Client-Side (chat.js)

Finally, we need to handle the broadcasted ‘typing’ messages on the client-side in chat.js. In each client, we need to listen for the ‘typing’ event broadcasted from the server. We use socket.on() again on the client-side to listen for this event.

Listening for the ‘typing’ Event and Displaying Feedback

When a client receives a broadcasted ‘typing’ event, it should display the “User is typing…” message in the feedback div. We can achieve this by modifying the innerHTML of the feedback div.

Inner HTML: innerHTML is a property of HTML elements that allows you to get or set the HTML content within that element. By setting innerHTML, you can dynamically change the content displayed on a webpage.

Here’s the client-side JavaScript code to handle the broadcasted ‘typing’ event and display the feedback:

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

This code snippet:

  • socket.on('typing', ...): Sets up an event listener on the client-side socket object to listen for the ‘typing’ event broadcasted from the server.
  • function (data) { ... }: Defines a callback function that executes when the ‘typing’ event is received. The data parameter contains the user’s handle that was broadcasted.
  • feedback.innerHTML = '<p><em>' + data + ' is typing a message...</em></p>';: This line updates the content of the feedback div.
    • feedback.innerHTML = ...: Sets the HTML content inside the feedback div.
    • '<p><em>' + data + ' is typing a message...</em></p>': Constructs an HTML string that includes:
      • <p>: A paragraph element to contain the message.
      • <em>: An emphasis element to italicize the text.
      • data: The user’s handle (username) received from the server.
      • ' is typing a message...': The static text of the message.

Clearing the Feedback Message After Sending

To enhance the user experience, we should clear the “User is typing…” message from the feedback div once the user actually sends their chat message. This prevents the feedback message from lingering unnecessarily. We can achieve this by resetting the innerHTML of the feedback div to an empty string within the event listener for the ‘chat’ event (the event triggered when a message is sent).

chatForm.addEventListener('submit', function(event){
    // ... existing code for emitting 'chat' message ...

    feedback.innerHTML = ''; // Clear the feedback message
});

By adding feedback.innerHTML = ''; within the chatForm submit event listener, we ensure that the “typing…” message is cleared from the feedback area as soon as the user sends their actual chat message.

Demonstration and Conclusion

Testing the Chat Application

With these code modifications in place, you can now run your WebSocket chat application and test the broadcasting feature. Open multiple browser windows or tabs, each representing a different user. When one user starts typing in the message input field, you should see the “[Username] is typing a message…” feedback appear in the other users’ chat windows in real-time. This demonstrates successful WebSocket broadcasting.

Further Exploration of WebSockets

This chapter has showcased the power of WebSocket broadcasting, specifically in implementing a real-time typing indicator. However, WebSockets are capable of much more than just chat applications. They are a versatile technology suitable for a wide range of real-time applications, including online gaming, collaborative tools, live data dashboards, and more.

We encourage you to explore WebSockets further and delve into more advanced features and applications. This tutorial provides a foundational understanding of WebSockets 101 and opens the door to creating even more interactive and dynamic web experiences.