November 19, 2019

Coding a ridiculously good-looking jQuery chat room

In this tutorial, you will learn how to build a good-looking chat app with jQuery. You’ll take things one step at a time, starting by coding the UI before finally allowing users to send and receive messages in real-time.

I want to show you how to build a complete chat experience, not just an overly simple demo. That is why we’ll also look at fetching message history so that when someone joins the group or reloads the page, they can see all the old messages and join the conversation.

Here’s a preview of what you’ll build:

And in case you want to jump straight into the jQuery chat room example code, here it is on GitHub.

A choice technology stack

Contrary to what you might have read, jQuery is still a highly relevant and highly useful tool which is actively maintained. While new technologies like React, Angular and Vue certainly have merit, jQuey is a nimble, reliable tool, which also has a place.

In this tutorial, we’ll use jQuery to manage our application state and in turn, update the DOM. We’ll keep the code as clean as we can and look for ways to simplify the application logic.

Sending and receiving data in real-time normally requires you to write code involving web sockets (for example, an Express server with socket.io) which can be time-consuming and depending on how you implemented them, unreliable. To make building a powerful chat easier, we’ll be use CometChat Pro which I’ll teach you about soon:

Coding the chat UI layout

In this tutorial, you will first start by building the chat UI without any network functionality. Once the foundation is in place, you can swap some “test” logic with networking code powered by CometChat.

To get started, open your command-line and create a new folder named jquery-chat-app (or whatever you like really):

// Create a new folder
mkdir jquery-chat-app

Navigate to the newly-created folder and create an index.html file within it. This newly created file will be the home for all code responsible for rendering the user interface layout for your chat application.

Within index.html, you will define the user interface layout, then connect it to a dummy chat service. Towards the end of the tutorial, you will replace the dummy chat service with CometChat and introduce network connectivity.

Here is the code you should paste into index.html to begin (don’t worry, I’ll explain it all next):

// ./index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title> jQuery Chat Application </title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abril+Fatface&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto&display=swap">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="chat-app-wrapper">
<nav>
<div class="nav-left-section">
<img src="./assets/logo.svg" class="logo">
<span id="title">Chat</span>
</div>
<div class="nav-right-section">
<span class="welcome-messsage" id="loggedInUsername">Oluyemi</span>
<img id="loggedInUserAvatar" src="https://data.cometchat.com/assets/images/avatars/captainamerica.png" class="avatar">
</div>
</nav>
<div class="chat">
<div class="container">
<div class="msg-header">
<div class="active">
<h5>
#General
</h5>
</div>
</div>
<div class="chat-page">
<div class="msg-inbox">
<div class="chats" id="chats">
<div class="msg-page" id="msg-page">
<div class="loading-message-container" id="loading-message-container">
<div class="spinner">
<svg width="100" height="100" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#003">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
</div>
</div>
<div id="empty-chat" class="text-center img-fluid empty-chat">
<div class="empty-chat-holder">
<img src="./assets/empty-state.svg" class="img-res" alt="empty chat image">
</div>
<div>
<h2>No new message?</h2>
<h6 class="empty-chat-sub-title">Send your first message below.</h6>
</div>
</div>
<div>
<div id="group-message-holder">

</div>
</div>
</div>
</div>
<div class="msg-bottom">
<form class="message-form" id="message-form">
<div class="input-group">
<input type="text" id="input-text" class="form-control message-input" placeholder="Type something" required/>
<div id="send-message-spinner" class="spinner">
<svg width="30" height="30" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#003">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
</div>
<input id="loggedInUID" type="hidden"/>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>

<!-- Chat scripts -->
<script src="js/chatService.js"></script>
<script src="js/script.js"></script>

</body>
</html>

The code snippet above might look a bit daunting, but let’s try to break it down. First not that you have not yet added any styles and so if you run the application (or if I took a screenshot), it wouldn’t look very smart.

What you have here is the markup for a basic chat box, chat input, and navigation bar to render the name and avatar of the currently logged in user. You also include the CDN files for Bootstrap, Font-Awesome, Google fonts and, of course, jQuery.

You also referenced three local files named chatService.js, script.js, and styles.css. These files do not exist yet (you’ll create them shortly). Within them, you will soon write the logic to make the local chat application functional.

Next, create three different subfolders called js , css and assets within your project folder.

Download the style.css file from the example source code and save it in the css subfolder. It should look something like this:

// TRUNCATED ./css/style.css

// DO NOT COPY AND PASTE. DOWNLOAD THE FULL FILE HERE https://github.com/cometchat-pro-tutorials/jquery-chat-app/blob/master/css/style.css

* {
box-sizing: border-box;
}
body {
color: #333;
font-size: 13px;
margin: 0;
width: 100%;
height: 100vh;
-webkit-font-smoothing: antialiased;
}
h3 {
font-family: 'Abril Fatface';
margin-bottom: 20px;
}
button {
border: none;
width: 100%;
margin: auto;
margin-top: 40px;
cursor: pointer;
padding: 10px 0;
background: #1B47DB;
font-size: 14px;
color: #fff;
border-radius: 4px;
box-shadow: 0 4px 8px 0 #CFDFFF, 0 6px 20px 0 #CFDFFF;
font-weight: 700;
transform: perspective(1px) translateZ(0);
}

Also download all the images here and save it in the assets folder as all the images will be needed for the application.

If you open the index.html file now, you will see a promising (but non-functional) chatbox like this:

Making the chat functional

To help you grab the full concept behind the implementation of chat functionality with CometChat, you will start with by creating a simple chat application with similar functions to what is achievable with CometChat but with dummy contents. The intention here is to allow you build proper foundation and then subsequently enhance it to make it more robust and functional.

Here, it is assumed that you have been authenticated as a user and can therefore participate in a chat session. You will start with dummy contents in a messageArray[] that you will create in the next section. You will then grab the message typed in the input field by the user and save it in the array. Immediately after that, the details of the new message will be appended to an HTML element through the usage of CSS selectors and the view will be updated with the new content.

To start, navigate to the js folder and create two new files named as chatService.js and script.js. Open chatService.js and add the following code in it:

// ./js/chatService.js

const chatService = function() {
$('#empty-chat').hide();
$('#group-message-holder').hide();
$('#loading-message-container').hide();
$('#send-message-spinner').hide();

let messageArray = [
{
username: "oluyemi",
message: "This is a new message"
},
{
username: "Demo",
message: "This is a new for demo"
},
{
username: "sample",
message: "Another reply from me"
}
];

if (messageArray.length < 1) {
$('#empty-chat').show();
$('#group-message-holder').hide();
} else {
$('#group-message-holder').show();
}

return {
fetchMessages: function() {
},
sendMessage: function(message){
},
onMessageReceived: function() {
},
scrollToBottom() {
const chat = document.getElementById("msg-page");
chat.scrollTo(0, chat.scrollHeight + 30);
}
}
}();

Here, you created a chatService() with the following methods:

  • fetchMessages(): this method will carry out the functionality of fetching messages from the messageArray[] or a different remote server, loop through the messages and then update the page with the appropriate details.
  • sendMessage(): we will use this to handle sending of messages
  • onMessageReceived(): once a message is received from a participant, this method will be used to update the view
  • scrollToBottom(): to scroll to the bottom of the page to view the latest message.

Also, if there are no messages in the messageArray[] you included a condition to check the length of the array and either show an empty chat or display the messages in the array.

Next, you need to start adding the appropriate content to these methods. Use the following code for the fetchMessages() method:

fetchMessages: function() {
$.each(messageArray, function(index, value) {
let messageList;
if (value.username !== "oluyemi") {
messageList = `
<div class="received-chats old-chats">
<div class="received-chats-img">
<img src="https://data.cometchat.com/assets/images/avatars/captainamerica.png" alt="Avatar" class="avatar">
</div>
<div class="received-msg">
<div class="received-msg-inbox">
<p>
<span id="message-sender-id">${value.username}</span><br />
${value.message}
</p>
</div>
</div>
</div>
`
} else {
messageList = `
<div class="outgoing-chats old-chats">
<div class="outgoing-chats-msg">
<p>${value.message}</p>
</div>
<div class="outgoing-chats-img">
<img src="https://data.cometchat.com/assets/images/avatars/captainamerica.png" alt="" class="avatar">
</div>
</div>
`
}
$('#group-message-holder').append(messageList);
});
this.scrollToBottom();
},

You looped through the messageArray[] and append its content to a div with an id selector of #group-message-holder. To identify the sender of a particular message, you only checked against one of the names from the dummy content in the messageArray[]. You will change this to identify an authenticated user instead once you are ready to make use of CometChat. More about that later in the tutorial.

Next, update the sendMessage() method with the following code:

sendMessage: function(message){
$('#send-message-spinner').show();
messageArray.push(message);
},

Quite self-explanatory right? This function takes the new message as an argument and first then shows a spinner to indicate the process of sending message and once the message is sent successfully, you will update the messageArray[] by using the javaScript push() method to push the new content into it.

Now, once a message is received, update the onMessageReceived() to handle replacement of contents of view:

onMessageReceived: function() {
$('#empty-chat').hide();
$('#group-message-holder').show();
$('#send-message-spinner').hide();
$.each(messageArray, function(index, value) {
let messageList;
if (value.username !== "oluyemi") {
messageList = `
<div class="received-chats old-chats">
<div class="received-chats-img">
<img src="https://data.cometchat.com/assets/images/avatars/captainamerica.png" alt="Avatar" class="avatar">
</div>
<div class="received-msg">
<div class="received-msg-inbox">
<p>
<span id="message-sender-id">${value.username}</span><br />
${value.message}
</p>
</div>
</div>
</div>
`
} else {
messageList = `
<div class="outgoing-chats ongoing old-chats">
<div class="outgoing-chats-msg">
<p>${value.message}</p>
</div>
<div class="outgoing-chats-img">
<img src="https://data.cometchat.com/assets/images/avatars/captainamerica.png" alt="" class="avatar">
</div>
</div>
`
}
$('#group-message-holder').append(messageList);
});
this.scrollToBottom();
},

The method above is similar to the fetchMessages() method that you created earlier, but with extra functionality of updating the page with the new contents of the messageArray[]. For this to work properly as planned, you will only invoke this function immediately after a new message has been sent and the messageArray[] is updated.

Creating all the required methods was the first step. Next, you will invoke the methods to complete the implementation. Open js/script.js and add the following code:

// ./js/script.js

$(document).ready(function() {
chatService.fetchMessages();

$('#message-form').submit(function(e) {
e.preventDefault();
let message = $('#input-text').val();
let text = {
username: "oluyemi",
message
}
$('.old-chats').remove();
chatService.sendMessage(text);
chatService.onMessageReceived();

$('#message-form').trigger('reset');
});
});

To successfully manipulate a web page with jQuery, you will have to wait until the document ready event has been fired. This will indicate that the page is ready and you can start making changes where and when necessary. From the preceding code snippet, you invoked the chatService.fetchMessages() method to fetch old messages from the messageArray[] if available and then you will receive a new message from the input field once the form is submitted. Lastly, you will invoke the chatService.sendMessage(text) method to process the form and finally update the view once the message has been received.

You can run the index.html and you will see that page has been updated with the dummy contents:

Now test it out by sending a message to start chat:

If you followed along this far, congratulations but we’re not done yet. At the moment, the chat app is functional but because there is no networking code, you cannot actually communicate with anyone else. Naturally, this raises a question about how to send and receive messages over the network in real-time. That is where CometChat comes in.

Introducing CometChat

CometChat is a platform for building chat features like voice, video, and text chat. There are idiomatic SDKs for Android, iOS, and JavaScript which all connect to the same real-time API.

Some features include:

  • Group chat
  • One on one chat
  • Message history
  • Typing indicators
  • Online indicators
  • Read and delivered indicators
  • Push notifications
  • jQuery chat bots
  • … And a whole lot more

Today, you will be using CometChat to power group chat and message history for that group.

To get started, head to your CometChat dashboard (you’ll need to create a free account if you haven’t already) and create a new v1 application called whatever you like:

This will take a few seconds. Once completed, you can drill into your newly-created application and copy your app ID and full access key from the API Keys menu:

Please be aware that in a real-world application, you should not be putting your full access key in the client-side code. You should create an authentication server, as described in this tutorial.

Back in the code, you now need to install and initialise CometChat. To install CometChat, update index.html:

// ./index.html

<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<!-- Include this -->
<script type="text/javascript" src="https://unpkg.com/@cometchat-pro/chat@1.8/CometChat.js"></script>

..
</body>
</html>

Next, go back to the chatService.js and clear the dummy contents within messageArray[] plus add the following functions:

// ./js/chatService.js

const chatService = function() {
...

let messageArray = [];

return {
initializeApp: function() {
},
authLoginUser: function(username) {
},
getLoggedInUser: function() {
},
...
}
}();

  • initializeApp(): this will be used to initialise CometChat with the APP_ID generated for your application and obtained from your CometChat dashboard. Once the application is successfully initialized, we will use a prompt to request the username of the user and proceed to authenticate the user.
  • authLoginUser(): this method will receive the username of a user as an argument and then call the login() method from the CometChat SDK to authenticate a user and then obtained the details of the currently loggedIn user.
  • getLoggedInUser(): to obtain the username and the unique UID of a user once authenticated.

With these empty functions in place, we can now rapidly flesh them out. Starting with initializeApp:

// ./js/chatService.js

const chatService = function() {
...

let messageArray = [];

return {
initializeApp: function() {
CometChat.init('YOUR_COMET_CHAT_APP_ID').then(
() => {
console.log("Initialization completed successfully");
const username = prompt(`Welcome to our jQuery chat demo powered by CometChat. Login with the username superhero1 or superhero2 and test the chat out. To create your own user, copy this link 'https://prodocs.cometchat.com/reference#createuser' and paste into your address-bar`);
this.authLoginUser(username);
},
error => {
console.log("Initialization failed with error:", error);
}
)
},
authLoginUser: function(username) {
},
getLoggedInUser: function() {
},
...
}
}();

Here, we prompt the user for their username before immediately passing this to the authLoginUser() method. Replace YOUR_COMET_CHAT_APP_ID placeholder with the APP_ID for your application on CometChat dashboard. Now, update the authLoginUser() method with the following code:

// ./js/chatService.js

const chatService = function() {
...

let messageArray = [];

return {
initializeApp: function() {
...
},
authLoginUser: function(username) {
let apiKey = "YOUR_COMET_CHAT_API_KEY";
$('#loading-message-container').show();
CometChat.login(username, apiKey).then(
() => {
console.log("Login successfully");
this.getLoggedInUser();
},
error => {
alert("Whops. Something went wrong. This commonly happens when you enter a username that doesn't exist. Check the console for more information")
console.log("Login failed with error:", error.code);
}
)
},
getLoggedInUser: function() {
},
...
}
}();

Here, the login() method from CometChat SDK was used to authenticate the user. This will automatically log the user into your application and obtain the details if such a user exists. Don’t forget to replace YOUR_COMET_CHAT_API_KEY placeholder with the API_KEY obtained from your CometChat dashboard for your application.

To get the details of the currently loggedIn user, update the getLoggedInUser() method as shown here:

// ./js/chatService.js

const chatService = function() {
...

let messageArray = [];

return {
initializeApp: function() {
...
},
authLoginUser: function(username) {
...
},
getLoggedInUser: function() {
CometChat.getLoggedinUser().then(
user => {
$('#loggedInUsername').text(user.name);
$('#loggedInUserAvatar').attr("src", user.avatar)
$('#loggedInUID').val(user.uid);
$('#loading-message-container').hide();
this.fetchMessages();
},
error => {
console.log(error);
}
)
},
...
}
}();

This code called the getLoggedinUser() method from CometChat library, obtains the details of the user and invokes the fetchMessages() method to retrieve messages from the group that the logged in user belongs to.

At this point, you want to improve the processes of fetching, sending and receiving incoming messages by leveraging CometChat infrastructure. You started this earlier by initializing CometChat in your app and then proceeded to retrieve the details of the authenticated user. Moving forward, you need to update the methods responsible for these processes within your script. Begin by opening ./js/chatService.js and replace the content of fetchMessages() function with the following code:

// ./js/chatService.js

fetchMessages: function() {
const messagesRequest = new CometChat.MessagesRequestBuilder()
.setLimit(100)
.build()
messagesRequest.fetchPrevious().then(
messages => {
messageArray = [...messageArray, ...messages];
if (messageArray.length < 1) {
$('#empty-chat').show();
$('#group-message-holder').hide();
} else {
$('#group-message-holder').show();
}
$.each(messageArray, function(index, value) {
let messageList;
let currentLoggedUID = $('#loggedInUID').val();

if (value.sender.uid != currentLoggedUID) {
messageList = `
<div class="received-chats old-chats">
<div class="received-chats-img">
<img src="${value.sender.avatar}" alt="Avatar" class="avatar">
</div>

<div class="received-msg">
<div class="received-msg-inbox">
<p>
<span id="message-sender-id">${value.sender.uid}</span><br />
${value.data.text}
</p>
</div>
</div>
</div>
`
} else {
messageList = `
<div class="outgoing-chats old-chats">
<div class="outgoing-chats-msg">
<p>${value.data.text}</p>
</div>
<div class="outgoing-chats-img">
<img src="${value.sender.avatar}" alt="" class="avatar">
</div>
</div>
`
}

$('#group-message-holder').append(messageList);
});
this.scrollToBottom();
},
error => {
console.log("Message fetching failed with error:", error);
}
)
},

This code snippet fetches messages in bulk using the messageRequestBuilder class of CometChat library and before updating the view with the previous messages, you also included a condition to check if there are any messages at all.

Next to send messages from your chat application, replace the contents of the sendMessage() functions with the following:

sendMessage: function(){
$('#send-message-spinner').show();
let receiverID = "supergroup";
let messageText = $('#input-text').val();
let messageType = CometChat.MESSAGE_TYPE.TEXT;
let receiverType = CometChat.RECEIVER_TYPE.GROUP;

let textMessage = new CometChat.TextMessage(
receiverID, messageText, messageType, receiverType
);

CometChat.sendMessage(textMessage).then(
message => {
$('#message-form').trigger('reset');
messageArray = [...messageArray, message];
$.each(messageArray, function(index, value) {
let messageList;
let currentLoggedUID = $('#loggedInUID').val();

if (value.sender.uid != currentLoggedUID) {
messageList = `
<div class="received-chats old-chats">
<div class="received-chats-img">
<img src="${value.sender.avatar}" alt="Avatar" class="avatar">
</div>

<div class="received-msg">
<div class="received-msg-inbox">
<p>
<span id="message-sender-id">${value.sender.uid}</span><br />
${value.data.text}
</p>
</div>
</div>
</div>
`
} else {
messageList = `
<div class="outgoing-chats old-chats">
<div class="outgoing-chats-msg">
<p>${value.data.text}</p>
</div>
<div class="outgoing-chats-img">
<img src="${value.sender.avatar}" alt="" class="avatar">
</div>
</div>
`
}

$('#group-message-holder').append(messageList);
});
this.onMessageReceived();
this.scrollToBottom();
},
error => {
console.log("Message sending failed with error:", error);
}
);
},

To send messages to CometChat server, you instantiated the TextMessage() object with the receiverID, messageText, messageType, and receiverType as the mandatory parameters. Once the message was posted to the server, you updated the view with the messageArray[].

Finally, you need to listen in realtime to messages once they are received by any participant in a group chat. To achieve this, you will update the onMessageReceived() function with the code to call the MessageListener() class from CometChat. Replace the content of the onMessageReceived() with the following code:

onMessageReceived: function() {
$('#empty-chat').hide();
$('#group-message-holder').show();
$('#send-message-spinner').hide();
let listenerID = "UNIQUE_LISTENER_ID";
CometChat.addMessageListener(
listenerID,
new CometChat.MessageListener({
onTextMessageReceived: textMessage => {
messageArray = [...messageArray, textMessage];
$('.old-chats').remove();
$.each(messageArray, function(index, value) {
let messageList;
let currentLoggedUID = $('#loggedInUID').val();

if (value.sender.uid != currentLoggedUID) {
messageList = `
<div class="received-chats old-chats">
<div class="received-chats-img">
<img src="${value.sender.avatar}" alt="Avatar" class="avatar">
</div>

<div class="received-msg">
<div class="received-msg-inbox">
<p>
<span id="message-sender-id">${value.sender.uid}</span><br />
${value.data.text}
</p>
</div>
</div>
</div>
`
} else {
messageList = `
<div class="outgoing-chats old-chats">
<div class="outgoing-chats-msg">
<p>${value.data.text}</p>
</div>
<div class="outgoing-chats-img">
<img src="${value.sender.avatar}" alt="" class="avatar">
</div>
</div>
`
}

$('#group-message-holder').append(messageList);
});
this.scrollToBottom();
}
})
)
},

You will update the chat view of the receiver(s) of a message within a group chat and scroll down to the bottom of the page to view the new message.

Just like before, having changed and restructured the initialisation process and enhanced your local application with CometChat, you need to update the ./js/scripts.js file to initialize the application once the DOM is ready and then proceed to listen for onSubmit event on the chat HTML form. To achieve this, replace the content of scripts.js with the following code:

// .js/scripts.js
$(document).ready(function() {
// Initialize app
chatService.initializeApp();
// Send message
$('#message-form').submit(function(e) {
e.preventDefault();
$('.old-chats').remove();
chatService.sendMessage();

$('#message-form').trigger('reset');
});
});

Test out the chat application

Open the index.html again or refresh the page if you’re tab hoarder! You’ll see this:

Next, open the application on two different browsers and log in as separate users. You can log in with any two of the test users provided by CometChat for every new application: superhero1, superhero2, superhero3 as shown here and start a chat session:

Conclusion

In this tutorial, you started with a basic chat app that cannot render contents or messages over the network and gradually enhanced it by bringing CometChat into the picture. As seen here, you were able to build a complete functional chat application using the jQuery library.

What to read next?