Over the last couple of days, I have revisited Socket.io because I realized that I did not understand its power and coolness while I was doing the Chat-App Project. I have decided to implement a few new features, including: making it responsive, adding sounds when a user sends or receives a message, notifying that someone is typing underneath the input bar, etc.
You can chek out the live chat here.
I decided to write a tutorial on how to include the notification that someone was typing because it had some fun jQuery involved as well as a good demonstration of how sockets work in the chat application. As I have felt the most benefit from tutorials that have both video and text, I did both. The video can be watched on my site and here is my written tutorial.
The first thing I needed to do in order to add this feature was break the problem down into smaller steps:
- To run a check if there was a change in the input field. If there was a change I would write something underneath the input box like : “the user is typing”
- Check to see if the user stopped typing. If the user stopped typing I wanted to clear the message underneath the input box.
- Broadcast to everyone these changes.
1. Check to see if there is a change in the input field:
jQuery has different event listeners. Two of those are keyup and keydown. Briefly the keyup event is sent to an element when a user releases a key, while the keydown event is sent when a user presses on a key. So when keys are being pressed we want to notify the HTML and change something in it. We already have out input box and submit button, so under that we want to create a spot that I can manipulate with jQuery in my HTML.
<!-- index.html -->
<form class = "col s12">
<div class="row input">
<div class="input-field col s9">
<input placeholder="Rock on..." id="m" type="text" />
</div>
<div class="col s3">
<button class="waves-effect waves-light btn send right">Send</button>
</div>
</div>
<div class="typing"></div> <!--spot to manipulate -->
</form>
I also needed to add some CSS so the text went where I wanted it to go:
//style.css
.typing {
margin-top: -33px;
color: black;
float: left;
font-size: 11px;
font-weight: bolder;
margin-left: 30px;
}
Great so now that we have somewhere to change the text depending on what is happening, we first want to create a function that runs when the user starts typing. When the user starts typing into the input box (which has an id=‘m’) we want to say: “user is typing”. So the jQuery will look something like:
<!-- index.html -->
<script>
$('#m').keydown(function(){
console.log('the user is writing something');
var v = "user is typing";
$('.typing').html(v);
});
</script>
So the '#m' refers to the input box with the id='m' and the '.typing' refers to the div we added that can be manipulated with the class='typing'. So now when the user starts typing it should look like this on Chat Application:
2. Check to see if the user stops typing:
Now that we can change the text when the user starts typing we also want to change it when the user stops. This is a bit trickier but not too rough, we need to create a timer, and if that timer reachers a set time, then we want to change the text: I changed this to "empty" for right now so I could see the change and then will make it an empty string "" when I know everything is working well.
We need to create two variables:
<!-- index.html -->
<script>
//the keydownfunction//
var typingTimer;
var doneTypingInterval = 5000;
</script>
The typingTimer will count for us, and the doneTypingInterval is set to 5 seconds (5000 miliseconds). Once these are declared we can use them in our keyup function. So when the user releases a key, wait 5 seconds, then change the text to "empty":
<!-- index.html -->
<script>
//the keydownfunction//
var typingTimer;
var doneTypingInterval = 5000;
$('#m').keyup(function(){
clearTimeout(typingTimer);
typingTimer=setTimeout(doneTyping, doneTypingInterval);
console.log("print that the user stopped typing");
function doneTyping(){
var v = "empty";
$('.typing').html(v);
}
});
</script>
There is one other important thing that I do here: run the clearTimeout(typingTimer) each time that there is a keyup function. That means that it resets the timer back to 0 each time there is a keyup event. If it is not cleared then "empty" will get printed even though the user is still typing (because it is checking every keyup event, not just the last one).
So now if we give this a test:
We can see that if a user starts typing it says "user is typing" and if they stop typing for 5 seconds, the text changes to "empty" which is what we wanted!!
3. Broadcast to everyone these changes:
Right now, we can only see the changes on the screen of the person who is typing (the person who is affecting the input box that we are running our jQuery checks on). This is where the fun part of Sockets come in!
So if we go back to the keydown function where we change the div to say: "user is typing", we can also send out an emit event that can be listened for on the server end:
<!-- index.html -->
<script>
$('#m').keydown(function(){
console.log('the user is writing something');
var v = "user is typing";
$('.typing').html(v);
socket.emit('change');
});
//the keyupfunction//
</script>
Then on the server side, I can listen for this "change" event (*sideNote at bottom explains how I name events) and send it out to everyone.
//index.js
socket.on('change', function(){
socket.broadcast.emit('changed')
//*sidenote on naming events at bottom
});
Now here I decided to use a broadcast emit, this means that all the sockets except the one from the user that is typing, will be get this event. If you would like to know more about the times of events that Socket.io has, I found this great list of events reference.
But for now, we will use this socket.broadcast.emit to send to all the clients except for the sender(the one typing). Now back on the client side we will have to listen for the "changed" event, to display the change in text for all the clients (except the one typing).
<!-- index.html -->
<script>
//the keydownfunction//
socket.on('changed', function(){
var v = "someone else is typing";
$('.typing').html(v);
});
//the keyupfunction//
</script>
So when you are typing, it will still look like:
But if someone else is typing, it looks like:
Pretty cool right? But its going to get so much better! Here is where it gets super cool! Because I have a variable for name which I link to the client(user) when they join, I can pass that variable through the sockets (no biggie).
<!-- index.html -->
<script>
$('#m').keydown(function(){
console.log('the user is writing something');
var v = "user is typing";
$('.typing').html(v);
socket.emit('change', name);
//we pass the name variable here to go to the server
});
socket.on('changed', function(name){
//we listen for the name from the servers changed event
var v = name +" is typing";
//then use the name variable in html
$('.typing').html(v);
});
//the keyupfunction//
</script>
And equally on the server side:
//index.js
socket.on('change', function(name){
//listen for the name coming from the change event
socket.broadcast.emit('changed', name);
//and then send it back with the changed event
});
So now it should work like this:
*SideNote: I like to make all events going from the client to the server to be present tense (ie. change), and all the events going from the server to the client to be past tesne (ie. changed). This makes it easier for me to read my code later and know what I was trying to do. But this is purely preference so you can name these events whatever you like as long as you are listening for what you are emitting.
comments powered by Disqus