Build a typing indicator with JavaScript
A basic understanding of JavaScript is needed to follow this tutorial.
A popular feature of chat applications is a typing indicator, which displays the name or username of who is currently typing. In this tutorial, we will walk through how to build a typing indicator in a chat application using Pusher with JavaScript.
Here’s how the final application will look:
We will focus on the implementation of the typing indicator feature of the application.
Now, let’s dive into how we built the typing indicator in the chat application.
Setup a Pusher account
We will be using Pusher for the realtime features of this chat application, so first off you’ll need to create a Pusher account. When you first log in, a pop-up dialogue appears as shown below:
If you already have an account, log in to the dashboard and click on the Create new app
button in the Your apps
to the left. Select VanillaJS for the front-end tech and Node.js for the backend tech. (Don’t worry, the tech stack you select doesn’t matter as you can always change it later. Its purpose is to generate starter code that you can use to start communicating with Pusher)
After creating the application, go to the App Keys
tab and copy your App ID, Key, and Secret credentials. We will use them later in the tutorial.
Setup the Node.js server
Now that you have your Pusher Keys, let’s get to building the realtime chat application.
First, generate a Node.js application using this command:
npm init -y
Next, install Express, Pusher and some other dependencies the server will be needing:
npm install --save body-parser ejs express pusher
When done, the dependency section of your package.json file should look like this:
"dependencies": {
"body-parser": "^1.16.0",
"ejs": "^2.5.5",
"express": "^4.14.1",
"pusher": "^1.5.1"
}
To serve our application we need to do three things:
- Set up Express and Pusher.
- Create routes to serve our application pages and listen for web requests.
- Start the Express server.
1. Setup Express and Pusher
Inside the server.js
file, we initialize Express and Pusher like this:
const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');
const path = require('path');
const Pusher = require('pusher');
const app = express();
//Initialize Pusher
const pusherConfig = {
appId: 'YOUR_PUSHER_APP_ID',
key: 'YOUR_PUSHER_KEY',
secret: 'YOUR_PUSHER_SECRET',
encrypted: true
};
const pusher = new Pusher(pusherConfig);
Remember to replace the parameters in the pusherConfig
object with the Pusher credentials you copied earlier from the Pusher dashboard.
2. Create routes to serve our application
Setup Express to serve our static files from the public
folder and to load our HTML views from the views
:
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.get('/', function(req, res) {
res.render('index', {
//pass pusher key to index.ejs for pusher client
pusherKey: pusherConfig.key
});
});
Next, we create a route that uses Pusher to broadcast a user_typing
event.
const chatChannel = 'anonymous_chat';
const userIsTypingEvent = 'user_typing';
app.post('/userTyping', function(req, res) {
const username = req.body.username;
pusher.trigger(chatChannel, userIsTypingEvent, {username: username});
res.status(200).send();
});
This route broadcasts the request’s username to everyone subscribed to the channel.
3. Start the Express Server
Then start the Express server to listen on the app port 3000
.
app.listen(3000, function () {
console.log('Node server running on port 3000');
});
Now we have the application server set up. Next, we develop the chat application’s user interface and functionalities.
Set up the Chat Application Web Page
The HTML for the chat application is in the views/index.ejs
file. First, we load jQuery, bootstrap and our custom style and JavaScript:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Who is typing functionality with Pusher</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700,900,200italic,300italic,400italic,600italic,700italic,900italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/css/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cuid/1.3.8/browser-cuid.min.js"></script>
<script src="https://js.pusher.com/4.0/pusher.min.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
</head>
There are three sections in the application’s user interface:
- Top Menu: This contains the application title and a section to show who is currently typing.
- Messages: This contains a list of all messages sent and received.
- Input: This contains the text field to input messages and also contains the send button.
And here is the HTML body for it:
<div class="chat_window">
<div class="top_menu">
<div class="is_typing">
<em id="user-is-typing"></em>
</div>
<div class="title">Anonymous Chat App</div>
</div>
<ul class="messages">
</ul>
<div class="input bottom_wrapper clearfix">
<div>
Your username: <em><span id="username">loading...</span></em>
</div>
<br/>
<div class="message_input_wrapper">
<input id="message-text-field" class="message_input" placeholder="Type your message here..." />
</div>
<button id="send-button" class="send_message">
<div class="text">Send</div>
</button>
</div>
</div>
Now that the user interface is set up, let us add the typing indicator to the application.
How the typing indicator will work
Before we proceed to the implementation, let me explain how the typing indicator functionality will work.
When the user starts typing in the message text field, the page sends a request to the server. The server then broadcasts this to everyone as a Pusher event. The page would also subscribe to the user’s typing event from Pusher and show a ‘user is typing’ message when a broadcast is received.
Therefore we can split this process into two steps:
- Publishing
user_typing
Event. - Subscribing to
user_typing
Event.
Now, open the public/js/app.js
file for their implementations.
1. Publishing user_typing
Event
First, we need to initialize Pusher and some other variables.
var pusher = new Pusher(PUSHER_KEY, {encrypted: true,});
var chatChannelName = 'anonymous_chat';
var userIsTypingEvent = 'user_typing';
var currentUsername = getCurrentUsername();
Next, we need to listen to the current user’s input events and publish their username.
var messageTextField = $('#message-text-field');
messageTextField.on('keyup', function(event) {
$.post('/userTyping', {username: username});
});
This would post a ‘user is typing’ event for each input in the message text field. While this is good, it is not optimal because every input will make a call to the server.
A better way to do this is to throttle the rate at which we post events to the server. Throttling is a technique used to ensure a particular code is executed only once within a specified time interval.
So let’s update our code to ensure we only post typing events to the server once every 0.2 seconds.
var messageTextField = $('#message-text-field');
var canPublish = true;
var throttleTime = 200; //0.2 seconds
messageTextField.on('keyup', function(event) {
if(canPublish) {
$.post('/userTyping', {username: username});
canPublish = false;
setTimeout(function() {
canPublish = true;
}, throttleTime);
}
});
To explain the code, the canPublish
boolean is set to false
immediately after we post a ‘user is typing’ request to the server, preventing us from posting more requests. We then set a timeout function that sets the canPublish
boolean to true
after 0.2 seconds, thus allowing us to post another request to the server. This way, we throttle the requests made to the server.
Next, let’s see how we will subscribe to the user_typing
event.
2. Subscribing to user_typing
Event
We need to subscribe to our chat channel on Pusher and bind to the user_typing
event like so:
var channel = pusher.subscribe(chatChannelName);
channel.bind(userIsTypingEvent, function(data) {
if(data.username !== currentUsername) {
$('#user-is-typing').html(data.username + 'is typing...');
}
});
So, if the username of the event is not the current users’ username, we show a ‘user is typing’ text on the web page.
The application now updates the web page with the username that is typing. But the ‘user is typing’ message needs to be cleared when the user stops typing or else the message stays forever (and we definitely don’t want that). An easy solution is to set a timer that clears the ‘user is typing’ message after some seconds of not receiving an event. From experience, a clear timer of 0.9 seconds has given the best results.
We can set the clear timer by doing the following:
var clearInterval = 900; //0.9 seconds
var clearTimerId;
channel.bind(userIsTypingEvent, function(data) {
if(data.username !== currentUsername) {
$('#user-is-typing').html(data.username + 'is typing...');
//restart timeout timer
clearTimeout(clearTimerId);
clearTimerId = setTimeout(function () {
//clear user is typing message
$('#user-is-typing').html('');
}, clearInterval);
}
});
To explain the code, the userIsTypingEvent
will clear the ‘user is typing’ message after 0.9 seconds of not receiving a broadcast.
Putting all the codes together we have:
var channel = pusher.subscribe(chatChannelName);
var clearInterval = 900; //0.9 seconds
var clearTimerId;
channel.bind(userIsTypingEvent, function(data) {
if(data.username !== currentUsername) {
$('#user-is-typing').html(data.username + 'is typing...');
//restart timeout timer
clearTimeout(clearTimerId);
clearTimerId = setTimeout(function () {
//clear user is typing message
$('#user-is-typing').html('');
}, clearInterval);
}
});
And there you have it. The chat application now has the functionality to display who’s currently typing.
Testing
First, ensure you have updated your Pusher credentials in the server.js
file. To run the application, execute the server.js
file using the following command:
node server.js
The application should be running now. Visit the chat web page at http://localhost:3000. To try out the typing indicator feature, open two of the chat web pages beside each other. When you start typing in one, you should notice the other window shows that you are currently typing.
Conclusion
In this tutorial, we saw how to build a typing indicator using Pusher with JavaScript. As you can see, it is trivial and easy to build such realtime features using Pusher.
21 February 2017
by Perfect Makanju