Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Node.js chat frontend and server
$(function () {
"use strict";
// for better performance - to avoid searching in DOM
var content = $('#content');
var input = $('#input');
var status = $('#status');
// my color assigned by the server
var myColor = false;
// my name sent to the server
var myName = false;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) {
content.html($('<p>', { text: 'Sorry, but your browser doesn\'t '
+ 'support WebSockets.'} ));
input.hide();
$('span').hide();
return;
}
// open connection
var connection = new WebSocket('ws://127.0.0.1:1337');
connection.onopen = function () {
// first we want users to enter their names
input.removeAttr('disabled');
status.text('Choose name:');
};
connection.onerror = function (error) {
// just in there were some problems with conenction...
content.html($('<p>', { text: 'Sorry, but there\'s some problem with your '
+ 'connection or the server is down.' } ));
};
// most important part - incoming messages
connection.onmessage = function (message) {
// try to parse JSON message. Because we know that the server always returns
// JSON this should work without any problem but we should make sure that
// the massage is not chunked or otherwise damaged.
try {
var json = JSON.parse(message.data);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
// NOTE: if you're not sure about the JSON structure
// check the server source code above
if (json.type === 'color') { // first response from the server with user's color
myColor = json.data;
status.text(myName + ': ').css('color', myColor);
input.removeAttr('disabled').focus();
// from now user can start sending messages
} else if (json.type === 'history') { // entire message history
// insert every single message to the chat window
for (var i=0; i < json.data.length; i++) {
addMessage(json.data[i].author, json.data[i].text,
json.data[i].color, new Date(json.data[i].time));
}
} else if (json.type === 'message') { // it's a single message
input.removeAttr('disabled'); // let the user write another message
addMessage(json.data.author, json.data.text,
json.data.color, new Date(json.data.time));
} else {
console.log('Hmm..., I\'ve never seen JSON like this: ', json);
}
};
/**
* Send mesage when user presses Enter key
*/
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) {
return;
}
// send the message as an ordinary text
connection.send(msg);
$(this).val('');
// disable the input field to make the user wait until server
// sends back response
input.attr('disabled', 'disabled');
// we know that the first message sent from a user their name
if (myName === false) {
myName = msg;
}
}
});
/**
* This method is optional. If the server wasn't able to respond to the
* in 3 seconds then show some error message to notify the user that
* something is wrong.
*/
setInterval(function() {
if (connection.readyState !== 1) {
status.text('Error');
input.attr('disabled', 'disabled').val('Unable to comminucate '
+ 'with the WebSocket server.');
}
}, 3000);
/**
* Add message to the chat window
*/
function addMessage(author, message, color, dt) {
content.prepend('<p><span style="color:' + color + '">' + author + '</span> @ ' +
+ (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
+ (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
+ ': ' + message + '</p>');
}
});
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";
// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';
// Port where we'll run the websocket server
var webSocketsServerPort = 1337;
// websocket and http servers
var webSocketServer = require('websocket').server;
var http = require('http');
/**
* Global variables
*/
// latest 100 messages
var history = [ ];
// list of currently connected clients (users)
var clients = [ ];
/**
* Helper function for escaping input strings
*/
function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
// Array with some colors
var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
// ... in random order
colors.sort(function(a,b) { return Math.random() > 0.5; } );
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName = false;
var userColor = false;
console.log((new Date()) + ' Connection accepted.');
// send back chat history
if (history.length > 0) {
connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
}
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
console.log((new Date()) + ' User is known as: ' + userName
+ ' with ' + userColor + ' color.');
} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from '
+ userName + ': ' + message.utf8Data);
// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history = history.slice(-100);
// broadcast message to all connected clients
var json = JSON.stringify({ type:'message', data: obj });
for (var i=0; i < clients.length; i++) {
clients[i].sendUTF(json);
}
}
}
});
// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer "
+ connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
}
});
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSockets - Simple chat</title>
<style>
* { font-family:tahoma; font-size:12px; padding:0px; margin:0px; }
p { line-height:18px; }
div { width:500px; margin-left:auto; margin-right:auto;}
#content { padding:5px; background:#ddd; border-radius:5px; overflow-y: scroll;
border:1px solid #CCC; margin-top:10px; height: 160px; }
#input { border-radius:2px; border:1px solid #ccc;
margin-top:10px; padding:5px; width:400px; }
#status { width:88px; display:block; float:left; margin-top:15px; }
</style>
</head>
<body>
<div id="content"></div>
<div>
<span id="status">Connecting...</span>
<input type="text" id="input" disabled="disabled" />
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="./frontend.js"></script>
</body>
</html>
@muthuselvamit

This comment has been minimized.

Copy link

commented May 22, 2012

Hi Martinsik,

I am able to perform chat when i use the url in the browser as "file:///D:/jsfiles/nodejs-chat-IMPORTANT/nodejs-chat/chat.html"

If i use the in the browser as 'http://127.0.0.1:1337/chat.html', i am not able to connect to the server. please let me know the way to access chat application by pointing browser as like 'http://127.0.0.1:1337/chat.html'

@notprathap

This comment has been minimized.

Copy link

commented Nov 10, 2012

something

@joshmiller83

This comment has been minimized.

Copy link

commented Nov 15, 2012

This works, if you change line 26 of frontend.html to
<script src="chat-frontend.js"></script>

@joshmiller83

This comment has been minimized.

Copy link

commented Nov 15, 2012

<script src="frontend.js"></script>
@joshmiller83

This comment has been minimized.

Copy link

commented Nov 15, 2012

hmm... change the filename from "frontend.js" to "chat-frontend.js"

@melvincarvalho

This comment has been minimized.

Copy link

commented Jan 4, 2013

Is there a license for this gist ... could I make a few changes and add it to my website?

@martinsik

This comment has been minimized.

Copy link
Owner Author

commented Jan 13, 2013

Feel free to use it as you want. Just if you were publishing it somewhere, please, provide a link back to this original source.

@moeHaydar

This comment has been minimized.

Copy link

commented Jan 26, 2013

joshmiller83:
The reason this didn't work is because when client.html request frontend.js the server doesnt return the file.
Why?
The author left the HTTP server handler empty.
Solution:
You need to modules:
var url = require("url");
var st = require('node-static');

var fileServer = new st.Server('./');

and change the handle to

var server = http.createServer(function(request, response) {
request.addListener('end', function () {
var _get = url.parse(request.url, true).query;
fileServer.serve(request, response);
});
});

Welcome

@Viper177

This comment has been minimized.

Copy link

commented May 22, 2015

Thanks for the script.

@AbdulManaf

This comment has been minimized.

Copy link

commented Aug 17, 2015

am not able to type on textfield (frontend.html)

@rayj00

This comment has been minimized.

Copy link

commented Feb 26, 2016

Very nice. Thanks.

@kalxasath

This comment has been minimized.

Copy link

commented Feb 27, 2016

if you are running the htlm as file on the browser then on frontend.html replace lines 25,26 with the following and everything runs fine

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="./chat-frontend.js"></script>

Martinsik thanks for sharing !

@kalxasath

This comment has been minimized.

Copy link

commented Feb 27, 2016

to serve the frontend.html direct from the chat-server.js you need to do these:

install finalhandler and serve-static with the npm
npm install finalhandler serve-static

in the file chat-server.js replace line 39

// Not important for us. We're writing WebSocket server, not HTTP server

with

    var done = finalhandler(request, response);
    serve(request, response, done);

at line 21 add these lines

var finalhandler = require('finalhandler');
var serveStatic = require('serve-static');

var serve = serveStatic("./");

int the file frontend.html replace line 26
<script src="./frontend.js"></script>
with
<script src="./chat-frontend.js"></script>

finally call the chat page with

http://127.0.0.1:1337/frontend.html

@jwaldron92

This comment has been minimized.

Copy link

commented Jun 22, 2016

Like why would your write; "//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" and
"./frontend.js">

When the file is "https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"
"./chat-frontend.js"

besides that, great job. Can you reference how to put this online with backend saving?

@Voakie

This comment has been minimized.

Copy link

commented Aug 2, 2016

Found a little typo:
('Unable to *comminucate* ' + 'with the WebSocket server.');

Great work nonetheless!

@ghost

This comment has been minimized.

Copy link

commented Aug 11, 2016

How are the username, color and other things trashed when user closes connection?

@ivandi1980

This comment has been minimized.

Copy link

commented Oct 2, 2016

Thanks kalxasath, it works for me.....
very nice chat...

@tentenponce

This comment has been minimized.

Copy link

commented Mar 15, 2017

Found a bug. Wrong splicing of clients due to handling of index.

Try to connect two clients on websocket
Refresh the first client
Refresh the second client
One of them will not be able to receive any messages due to their connection has been spliced on disconnect.

Solution:

connection.on('close', function(connection) {
        if (userName !== false && userColor !== false) {
            console.log((new Date()) + " Peer "
                + connection.remoteAddress + " disconnected.");
            // loop to the clients and compare remote address to be removed
           for (var i = 0; i < clients.length; i ++) {
                if (connection.remoteAddress == clients[i].remoteAddress) { //compare remote address to remove from the disconnecting client
                     clients.splice(i, 1);
                }
           }
            // push back user's color to be reused by another user
            colors.push(userColor);
        }
    })

Thanks by the way for this tutorial, very helpful :)

@mingyun

This comment has been minimized.

Copy link

commented Apr 1, 2017

nice

@ghost

This comment has been minimized.

Copy link

commented May 15, 2017

Found a bug in tentenponce's patch.

Everything appears to be a working fine, only because clients.splice(i, 1) is never called, ever.. (note that 'connection' is being passed into the callback function.. thus connection.remoteAddress is always undefined) and therefore no client splicing, and the clients array will become gigantic over time. This fix should do the trick (note it is no longer ensuring userName and userColor are true, because if someone was sitting there refreshing the page, that would keep pushing new connections to the client array.)

connection.on('close', function(e) {
  for (var i = 0; i < clients.length; i ++) {
    if ((connection.remoteAddress == clients[i].remoteAddress) 
      && (connection.socket._peername.port == clients[i].socket._peername.port)) {
      clients.splice(i, 1)
    }
  }
  colors.push(userColor)
});
@rayj00

This comment has been minimized.

Copy link

commented Jun 8, 2017

I started to play with this script about a year ago, but had to abandon in lieu of more pressing issues...
On the console, I get Thu Jun 08 2017 16:45:10 GMT-0400 (EDT) Server is listening on port 1337.

How ever on the client, I am getting "Sorry, bit there's some problem with your connection or the server is down" in the chat response box.
In the chat enter box I am getting Error Unable to communicate with the Websocket server.

Any ideas?

Ray

@DarkVidead

This comment has been minimized.

Copy link

commented Jun 9, 2017

Same here!

@rayj00

This comment has been minimized.

Copy link

commented Jun 20, 2017

So I am trying again to get this to work. I have a new Ubuntu 16.04.2.
I put all three files (chat-frontend.js, chat-server.js and frontend.html in the same directory.
Then I did sudo node chat-server.js and got: Tue Jun 20 2017 13:11:29 GMT-0400 (EDT) Server is listening on port 1337
Browsing to http://192.168.0.15:1337 just sits there rolling, Waiting for 192.168.0.15...

And yes I renamed the frontend.js file to chat-frontend.js

Ideas?

Ray

@rayj00

This comment has been minimized.

Copy link

commented Jun 21, 2017

Tried the above suggested fixes. Nothing helps.
I know I had this script farther along about a year ago.
I know I could access the html page, but I could not get a connection to the server.
Now I am stuck. It will not even serve up the html?

And BTW, the only change I remember doing is the filename change from frontend.js to chat-frontend.js.
So I don't know what all the above changes are for?

Can anyone provide working code and just how you store the files and call the chat.

@rayj00

This comment has been minimized.

Copy link

commented Jun 21, 2017

Ah....forget the above post. I fat fingered.

So now I am back to
chat error

@rayj00

This comment has been minimized.

Copy link

commented Jun 21, 2017

Ok got up to entering my name but after I enter my name, it still says: Choose Name: Then eventually says Unable to comminucate with the WebSocket server. in the text entry box. But the message display area is blank, so I assume that's good?

@rayj00

This comment has been minimized.

Copy link

commented Jun 21, 2017

Ok, now I cannot get beyond the "Choose Name" step. If I just let it sit at Choose Name (after entering a name) it eventually ends up with Error Unable to comminucate with the WebSocket server.

@Globik

This comment has been minimized.

Copy link

commented Jul 31, 2017

@rayj00 install express.js, change the server code and don't sack any more. Also use better npm install ws. It's better than websocket.js.

@aatronics

This comment has been minimized.

Copy link

commented Aug 14, 2017

Thanks for such a nice code

@hotdang-ca

This comment has been minimized.

Copy link

commented Sep 5, 2017

Just wanted to put this here, for those who don't really like jQuery:

Pure Javascript chat-frontend.js

Excellent article, helped me break away from websocket abstractions such as Socket.io or Pusher.

@rayj00

This comment has been minimized.

Copy link

commented Jan 5, 2018

Is there a working demo of this chat, complete with all the fixes?

Ray

@rayj00

This comment has been minimized.

Copy link

commented Jan 13, 2018

Working..... but chat messages are not deleted when all users disconnect? When a user comes back in, the old messages are there?
Ideas?

Ray

@bluerid

This comment has been minimized.

Copy link

commented Jan 23, 2018

WebSocket-Node module provides a WebSocketRouter that can be used to define routes. However, I wasn't able to find a way to pass path parameters using the WebSocketRouter. Do you happen to know how I can do that? Is that even possible? I don't intend to use express.
https://github.com/theturtle32/WebSocket-Node

@Planeur

This comment has been minimized.

Copy link

commented Mar 25, 2018

i think this works well.

connection.on('close', function(connection) {
// find the destroyed connection
for (var i = 0; i < clients.length; i ++) {

            if (clients[i].socket._readableState.destroyed==true) { //compare remote address to remove from the disconnecting client
                var remoteAdresse= clients[i].remoteAddress.slice(7);
                console.log((new Date()) + " Peer "+ remoteAdresse + " disconnected."); 
                clients.splice(i, 1);
            }
       }
        // push back user's color to be reused by another user
        colors.push(userColor);
    
})
@Planeur

This comment has been minimized.

Copy link

commented Mar 25, 2018

when the connection is destroyed connection.remoteAddress is undefined.

@jonathan-annett

This comment has been minimized.

Copy link

commented Apr 4, 2018

tweaked the files a bit to allow node to server the files automajically
server

client

chat-server.js

    // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
    "use strict";
    
    // Optional. You will see this name in eg. 'ps' or 'top' command
    process.title = 'node-chat';
    
    // Port where we'll run the websocket server
    var webSocketsServerPort = 1337;
    
    // websocket and http servers
    var webSocketServer = require('websocket').server;
    var http = require('http');
    var fs = require('fs');
    
    /**
     * Global variables
     */
    // latest 100 messages
    var history = [ ];
    // list of currently connected clients (users)
    var clients = [ ];
    
    var http_files = {};
    [
        ["/jquery.min.js","application/javascript"],
        ["/frontend.js","application/javascript"],
        ["/frontend.html","text/html"]
    ].forEach(function(fn){
        http_files[fn[0]]={
            content : fs.readFileSync('.'+fn[0]).toString(),
            contentType : fn[1]
        };
    });
    
    http_files["/"]=http_files["/frontend.html"];
    http_files["/index.html"]=http_files["/frontend.html"];
    
    /**
     * Helper function for escaping input strings
     */
    function htmlEntities(str) {
        return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;')
                          .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    }
    
    // Array with some colors
    var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
    // ... in random order
    colors.sort(function(a,b) { return Math.random() > 0.5; } );
    
    /**
     * HTTP server
     */
    var server = http.createServer(function(request, response) {
        // this doubles as a way to serve the fies, and a connection for websocket to use
        var file = http_files[request.url];
        if (file) {
            response.writeHeader(200,{"content-type" : file.contentType});
            response.write(file.content);
            return response.end();
        }
        response.writeHeader(404,{"content-type" : "text/plain"});
        response.write("not found");
        return response.end();
    
    });
    
    server.listen(webSocketsServerPort, function() {
        console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
    });
    
    /**
     * WebSocket server
     */
    var wsServer = new webSocketServer({
        // WebSocket server is tied to a HTTP server. WebSocket request is just
        // an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
        httpServer: server
    });
    
    // This callback function is called every time someone
    // tries to connect to the WebSocket server
    wsServer.on('request', function(request) {
        console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
    
        // accept connection - you should check 'request.origin' to make sure that
        // client is connecting from your website
        // (http://en.wikipedia.org/wiki/Same_origin_policy)
        var connection = request.accept(null, request.origin); 
        // we need to know client index to remove them on 'close' event
        var index = clients.push(connection) - 1;
        var userName = false;
        var userColor = false;
    
        console.log((new Date()) + ' Connection accepted.');
    
        // send back chat history
        if (history.length > 0) {
            connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
        }
    
        // user sent some message
        connection.on('message', function(message) {
            if (message.type === 'utf8') { // accept only text
                if (userName === false) { // first message sent by user is their name
                    // remember user name
                    userName = htmlEntities(message.utf8Data);
                    // get random color and send it back to the user
                    userColor = colors.shift();
                    connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
                    console.log((new Date()) + ' User is known as: ' + userName
                                + ' with ' + userColor + ' color.');
    
                } else { // log and broadcast the message
                    console.log((new Date()) + ' Received Message from '
                                + userName + ': ' + message.utf8Data);
                    
                    // we want to keep history of all sent messages
                    var obj = {
                        time: (new Date()).getTime(),
                        text: htmlEntities(message.utf8Data),
                        author: userName,
                        color: userColor
                    };
                    history.push(obj);
                    history = history.slice(-100);
    
                    // broadcast message to all connected clients
                    var json = JSON.stringify({ type:'message', data: obj });
                    for (var i=0; i < clients.length; i++) {
                        clients[i].sendUTF(json);
                    }
                }
            }
        });
    
        // user disconnected
        connection.on('close', function(connection) {
            if (userName !== false && userColor !== false) {
                console.log((new Date()) + " Peer "
                    + connection.remoteAddress + " disconnected.");
                // remove user from the list of connected clients
                clients.splice(index, 1);
                // push back user's color to be reused by another user
                colors.push(userColor);
            }
        });
    
    });

frontend.js (note same content - just renamed from chat-frontend.js, included here for convenience)

    $(function () {
        "use strict";
    
        // for better performance - to avoid searching in DOM
        var content = $('#content');
        var input = $('#input');
        var status = $('#status');
    
        // my color assigned by the server
        var myColor = false;
        // my name sent to the server
        var myName = false;
    
        // if user is running mozilla then use it's built-in WebSocket
        window.WebSocket = window.WebSocket || window.MozWebSocket;
    
        // if browser doesn't support WebSocket, just show some notification and exit
        if (!window.WebSocket) {
            content.html($('<p>', { text: 'Sorry, but your browser doesn\'t '
                                        + 'support WebSockets.'} ));
            input.hide();
            $('span').hide();
            return;
        }
    
        // open connection
        var connection = new WebSocket('ws://127.0.0.1:1337');
    
        connection.onopen = function () {
            // first we want users to enter their names
            input.removeAttr('disabled');
            status.text('Choose name:');
        };
    
        connection.onerror = function (error) {
            // just in there were some problems with conenction...
            content.html($('<p>', { text: 'Sorry, but there\'s some problem with your '
                                        + 'connection or the server is down.' } ));
        };
    
        // most important part - incoming messages
        connection.onmessage = function (message) {
            // try to parse JSON message. Because we know that the server always returns
            // JSON this should work without any problem but we should make sure that
            // the massage is not chunked or otherwise damaged.
            try {
                var json = JSON.parse(message.data);
            } catch (e) {
                console.log('This doesn\'t look like a valid JSON: ', message.data);
                return;
            }
    
            // NOTE: if you're not sure about the JSON structure
            // check the server source code above
            if (json.type === 'color') { // first response from the server with user's color
                myColor = json.data;
                status.text(myName + ': ').css('color', myColor);
                input.removeAttr('disabled').focus();
                // from now user can start sending messages
            } else if (json.type === 'history') { // entire message history
                // insert every single message to the chat window
                for (var i=0; i < json.data.length; i++) {
                    addMessage(json.data[i].author, json.data[i].text,
                               json.data[i].color, new Date(json.data[i].time));
                }
            } else if (json.type === 'message') { // it's a single message
                input.removeAttr('disabled'); // let the user write another message
                addMessage(json.data.author, json.data.text,
                           json.data.color, new Date(json.data.time));
            } else {
                console.log('Hmm..., I\'ve never seen JSON like this: ', json);
            }
        };
    
        /**
         * Send mesage when user presses Enter key
         */
        input.keydown(function(e) {
            if (e.keyCode === 13) {
                var msg = $(this).val();
                if (!msg) {
                    return;
                }
                // send the message as an ordinary text
                connection.send(msg);
                $(this).val('');
                // disable the input field to make the user wait until server
                // sends back response
                input.attr('disabled', 'disabled');
    
                // we know that the first message sent from a user their name
                if (myName === false) {
                    myName = msg;
                }
            }
        });
    
        /**
         * This method is optional. If the server wasn't able to respond to the
         * in 3 seconds then show some error message to notify the user that
         * something is wrong.
         */
        setInterval(function() {
            if (connection.readyState !== 1) {
                status.text('Error');
                input.attr('disabled', 'disabled').val('Unable to comminucate '
                                                     + 'with the WebSocket server.');
            }
        }, 3000);
    
        /**
         * Add message to the chat window
         */
        function addMessage(author, message, color, dt) {
            content.prepend('<p><span style="color:' + color + '">' + author + '</span> @ ' +
                 + (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
                 + (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
                 + ': ' + message + '</p>');
        }
    });

frontend.html

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>WebSockets - Simple chat</title>
    
            <style>
            * { font-family:tahoma; font-size:12px; padding:0px; margin:0px; }
            p { line-height:18px; }
            div { width:500px; margin-left:auto; margin-right:auto;}
            #content { padding:5px; background:#ddd; border-radius:5px; overflow-y: scroll;
                       border:1px solid #CCC; margin-top:10px; height: 160px; }
            #input { border-radius:2px; border:1px solid #ccc;
                     margin-top:10px; padding:5px; width:400px;  }
            #status { width:88px; display:block; float:left; margin-top:15px; }
            </style>
        </head>
        <body>
            <div id="content"></div>
            <div>
                <span id="status">Connecting...</span>
                <input type="text" id="input" disabled="disabled" />
            </div>
    
            <script src="jquery.min.js"></script>
            <script src="frontend.js"></script>
        </body>
    </html>

finally, do this in the same folder : wget https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js to cache jquery

@rolandcoops

This comment has been minimized.

Copy link

commented Apr 10, 2018

Great tutorial, thanks for making it

@JensThanx

This comment has been minimized.

Copy link

commented May 11, 2018

Thanks for the tutorial Martin.

@jonathan-annett
Thanks for the new version. It works fine for me.
For those using Windows/Powershell, the final command would be something like:
wget https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js -OutFile jquery.min.js.

@MarcoLutze

This comment has been minimized.

Copy link

commented May 30, 2018

great script, it works fine! Thank you! My question: how do i remove the connection on the server, f.e if you close the browser, or reloading the browser creates a new connection without closing the old connection.What am i doing wrong?

@ghost

This comment has been minimized.

Copy link

commented Jul 10, 2018

Can we use in different systems

@nickvargish

This comment has been minimized.

Copy link

commented Oct 23, 2018

Hugely useful resource, thanks very much for posting it.

I think the method of removing clients is suspect. If you remove an element from an array, the index of the other (higher numbered) items will change.
l = [0, 1, 2, 3, 4];
console.log(l[3]) //3
l.splice(2, 1)
console.log(l[3]) //4
You could store the connections in a hash using some kind of unique (time based maybe) id.

@srivanigorthi

This comment has been minimized.

Copy link

commented Oct 24, 2018

should we call frontend.html ? Where is chat.html file?

@kwyni

This comment has been minimized.

Copy link

commented Nov 4, 2018

In the chat-server.js, the line
content : fs.readFileSync('.'+fn[0]).toString(),
should be replaced by this:
content : fs.readFileSync(__dirname + fn[0]).toString(),

for services to have a better chance to run (to prevent from file not found)

@pan-rzeznik

This comment has been minimized.

Copy link

commented Nov 10, 2018

should we call frontend.html ? Where is chat.html file?

frontend.html => chat.html. It's work.

@ajhstn

This comment has been minimized.

Copy link

commented Feb 25, 2019

Hi @martinsik this is excellent. I have been trying to basically replicate this using pure http2. Can i please share my code with you and ask for your help?

Specifically i am stuck when the client enters text into the input field, then hits the submit button, i can handle that request in the server code, but i don't know how to stream it back to the client(s).

@JanaBrahimi

This comment has been minimized.

Copy link

commented Feb 26, 2019

May I ask what this is for?

@fakruboss

This comment has been minimized.

Copy link

commented Mar 24, 2019

Hi Martinsik,

I am able to perform chat when i use the url in the browser as "file:///D:/jsfiles/nodejs-chat-IMPORTANT/nodejs-chat/chat.html"

If i use the in the browser as 'http://127.0.0.1:1337/chat.html', i am not able to connect to the server. please let me know the way to access chat application by pointing browser as like 'http://127.0.0.1:1337/chat.html'

host the frontend.html using npm http-server, and then you can access the chat application using the ip provided by the http-server

steps:
npm install http-server

cd frontend.js (give the path where the file is located)

http-server

@Yayo98

This comment has been minimized.

Copy link

commented May 29, 2019

How Can I use it in another computer?

@msreddy09

This comment has been minimized.

Copy link

commented Jul 2, 2019

Use the following code to work with http://localhost:1337/frontend.html

var basePath = __dirname;
var http = require('http');
var fs = require('fs');
var path = require('path');
http.createServer(function(req, res) {
    var stream = fs.createReadStream(path.join(basePath, req.url));
    stream.on('error', function() {
        res.writeHead(404);
        res.end();
    });
    stream.pipe(res);
}) 
@sonvuhwg

This comment has been minimized.

Copy link

commented Aug 7, 2019

install finalhandler and serve-static with the npm
npm install finalhandler serve-static

thanks i succeed

@sooley

This comment has been minimized.

Copy link

commented Sep 27, 2019

Thank for this source

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.