In part two of this two-part tutorial, we will be creating the jQuery and CSS front end of our AJAX Web Chat. In the first part, we discussed the PHP & MySQL side. You can read the first part here.

Now lets continue from where we left off last time.

CSS

The chat styles are self-contained and reside in chat.css. These styles are independent from the rest of the page, so it is easier to embed the chat window into an existing website. You just need to include the HTML markup we discussed last week, and include the stylesheet and JavaScript files.

chat.css – Part 1

/* Main chat container */ #chatContainer{ width:510px; margin:100px auto; position:relative; } /* Top Bar */ #chatTopBar{ height:40px; background:url('../img/solid_gray.jpg') repeat-x #d0d0d0; border:1px solid #fff; margin-bottom:15px; position:relative; color:#777; text-shadow:1px 1px 0 #FFFFFF; } #chatTopBar .name{ position:absolute; top:10px; left:40px; } #chatTopBar img{ left:9px; position:absolute; top:8px; } /* Chats */ #chatLineHolder{ height:360px; width:350px; margin-bottom:20px; outline:none; } .chat{ background:url('../img/chat_line_bg.jpg') repeat-x #d5d5d5; min-height:24px; padding:6px; border:1px solid #FFFFFF; padding:8px 6px 4px 37px; position:relative; margin:0 10px 10px 0; } .chat:last-child{ margin-bottom:0; } .chat .gravatar{ background:url('http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=23') no-repeat; left:7px; position:absolute; top:7px; } .chat img{ display:block; visibility:hidden; } We start off by styling the #chatContainer div. It is horizontally centered on the page, with the help of an auto margin. As you saw in the previous part of the tutorial, this div is further divided into a top bar, chats area, user area, and the bottom bar.

The top bar displays the user’s login information. It is assigned a relative positioning so that the avatar, name and logout button can be positioned accordingly.

After this comes the div that holds all of the chats – #chatLineHolder. This div has a fixed width and height, and as you will see in the jQuery part of this tutorial, we are using the jScrollPane plugin to turn it into a fancy scrollable area with custom sidebars.

An AJAX Web Chat With PHP, MySQL & jQuery


chat.css – Part 2

/* Chat User Area */ #chatUsers{ background-color:#202020; border:1px solid #111111; height:360px; position:absolute; right:0; top:56px; width:150px; } #chatUsers .user{ background:url('http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size=30') no-repeat 1px 1px #444444; border:1px solid #111111; float:left; height:32px; margin:10px 0 0 10px; width:32px; } #chatUsers .user img{ border:1px solid #444444; display:block; visibility:hidden; } /* Bottom Bar */ #chatBottomBar{ background:url('../img/solid_gray.jpg') repeat-x #d0d0d0; position:relative; padding:10px; border:1px solid #fff; } #chatBottomBar .tip{ position:absolute; width:0; height:0; border:10px solid transparent; border-bottom-color:#eeeeee; top:-20px; left:20px; } #submitForm{ display:none; } In the second part we style the #chatUsers container and the user divs. Each active chat user is represented by a 32 by 32 px gravatar. The default one is defined as a background, and when the real background images are loaded, they are shown above them. This prevents the annoying flickering that would usually occur before the image is loaded.

The rest of the code deals with the bottom bar and the submit forms. You may find interesting the way the .tip div is turned into a pure CSS triangle using a zero height and width, along with a large border value. We’ve used this trick in previous tutorials as well.

chat.css – Part 3

/* Overriding the default styles of jScrollPane */ .jspVerticalBar{ background:none; width:20px; } .jspTrack{ background-color:#202020; border:1px solid #111111; width:3px; right:-10px; } .jspDrag { background:url('../img/slider.png') no-repeat; width:20px; left:-9px; height:20px !important; margin-top:-5px; } .jspDrag:hover{ background-position:left bottom; } /* Additional styles */ #chatContainer .blueButton{ background:url('../img/button_blue.png') no-repeat; border:none !important; color:#516D7F !important; display:inline-block; font-size:13px; height:29px; text-align:center; text-shadow:1px 1px 0 rgba(255, 255, 255, 0.4); width:75px; margin:0; cursor:pointer; } #chatContainer .blueButton:hover{ background-position:left bottom; } In the last part of the code, we override the default styling of the jScrollPane div. By default it is shown with purple scrollbars, which is not very suitable for our design. Instead of coding our own stylesheet from scratch, we just include the default one and override some of the rules.

Lastly, you can see the styles of the blue button. You can assign this class to any regular anchor or button, and you will get a pretty blue button.

jQuery

Moving to the last step of this tutorial – the jQuery code. The chat works by listening for events on the login and submit forms (and the logout button), and by scheduling AJAX request back to the server for checking for new chats and users.

As you saw in the first part of the tutorial last week, on the PHP side the AJAX requests are handled by ajax.php. jQuery issues a number of AJAX requests:


  • Logging a user in: this is done by a single POST request;
  • Logging a user out: also a single POST request;
  • Checking for logged in users: this is done once every 15 seconds;
  • Checking for new chats: a GET request is fired every second. This could potentially mean a heavy load on your webserver, this is why the script is optimized on the back end, and depending on the activity of the chat, requests are decreased to one every 15 seconds.

As you will see in the code below, we’ve definedcustom wrapper forjQuery’s $.get and $.post AJAX functions, which will aid us in not having to fill in all the lengthy parameters for issuing a request.

Also, all of the chat code is organized in a single object called chat. It consists of a number of useful methods, which you will see in the fragments below.

script.js – Part 1

$(document).ready(function(){ chat.init(); }); var chat = { // data holds variables for use in the class: data : { lastID : 0, noActivity : 0 }, // Init binds event listeners and sets up timers: init : function(){ // Using the defaultText jQuery plugin, included at the bottom: $('#name').defaultText('Nickname'); $('#email').defaultText('Email (Gravatars are Enabled)'); // Converting the #chatLineHolder div into a jScrollPane, // and saving the plugin's API in chat.data: chat.data.jspAPI = $('#chatLineHolder').jScrollPane({ verticalDragMinHeight: 12, verticalDragMaxHeight: 12 }).data('jsp'); // We use the working variable to prevent // multiple form submissions: var working = false; // Logging a person in the chat: $('#loginForm').submit(function(){ if(working) return false; working = true; // Using our tzPOST wrapper function // (defined in the bottom): $.tzPOST('login',$(this).serialize(),function(r){ working = false; if(r.error){ chat.displayError(r.error); } else chat.login(r.name,r.gravatar); }); return false; }); The purpose of the init() method is to bind all event handlers for the chat and start the timeout functions that are used to schedule the checks for new chats and online users. You can see that we’ve used our own wrapper functions – $.tzGET and $.tzPOST. These lift the burden of having to specify a long list of parameters and targets for the ajax requests.

script.js – Part 2

// Submitting a new chat entry: $('#submitForm').submit(function(){ var text = $('#chatText').val(); if(text.length == 0){ return false; } if(working) return false; working = true; // Assigning a temporary ID to the chat: var tempID = 't'+Math.round(Math.random()*1000000), params = { id : tempID, author : chat.data.name, gravatar : chat.data.gravatar, text : text.replace(//g,'>') }; // Using our addChatLine method to add the chat // to the screen immediately, without waiting for // the AJAX request to complete: chat.addChatLine($.extend({},params)); // Using our tzPOST wrapper method to send the chat // via a POST AJAX request: $.tzPOST('submitChat',$(this).serialize(),function(r){ working = false; $('#chatText').val(''); $('div.chat-'+tempID).remove(); params['id'] = r.insertID; chat.addChatLine($.extend({},params)); }); return false; }); // Logging the user out: $('a.logoutButton').live('click',function(){ $('#chatTopBar > span').fadeOut(function(){ $(this).remove(); }); $('#submitForm').fadeOut(function(){ $('#loginForm').fadeIn(); }); $.tzPOST('logout'); return false; }); // Checking whether the user is already logged (browser refresh) $.tzGET('checkLogged',function(r){ if(r.logged){ chat.login(r.loggedAs.name,r.loggedAs.gravatar); } }); // Self executing timeout functions (function getChatsTimeoutFunction(){ chat.getChats(getChatsTimeoutFunction); })(); (function getUsersTimeoutFunction(){ chat.getUsers(getUsersTimeoutFunction); })(); }, In the second part of the script, we continue with binding event listeners. In the submit form, you can see that when the user adds a new chat, a temporary one is created and shown immediately, without waiting for the AJAX request to complete. Once the write has completed, the temporary chat is removed from the screen. This gives users the feeling that the chat is lightning fast, while the real write is performed in the background.

Near the end of the init method, we run two self executing named functions. The functions themselves are passed as parameters to the respective chat.getChats() or chat.getUsers() method, so that additional timeouts can be scheduled (you can see this in part 5 of the code).

script.js – Part 3

// The login method hides displays the // user's login data and shows the submit form login : function(name,gravatar){ chat.data.name = name; chat.data.gravatar = gravatar; $('#chatTopBar').html(chat.render('loginTopBar',chat.data)); $('#loginForm').fadeOut(function(){ $('#submitForm').fadeIn(); $('#chatText').focus(); }); }, // The render method generates the HTML markup // that is needed by the other methods: render : function(template,params){ var arr = []; switch(template){ case 'loginTopBar': arr = [ '', '',params.name, 'Logout']; break; case 'chatLine': arr = [ ''+ '', '',params.author, ':',params.text, '',params.time,'
']; break; case 'user': arr = [ '
' ]; break; } // A single array join is faster than // multiple concatenations return arr.join(''); }, Here the render() method deserves most of our attention. What it does, is assemble a template depending on the passed template parameter. The method then creates and returns the requested HTML code, incorporating the values of the second parameter – the params object as needed. This is used by most of the othermethods discussed here.

script.js – Part 4

// The addChatLine method ads a chat entry to the page addChatLine : function(params){ // All times are displayed in the user's timezone var d = new Date(); if(params.time) { // PHP returns the time in UTC (GMT). We use it to feed the date // object and later output it in the user's timezone. JavaScript // internally converts it for us. d.setUTCHours(params.time.hours,params.time.minutes); } params.time = (d.getHours() < 10 ? '0' : '' ) + d.getHours()+':'+ (d.getMinutes() < 10 ? '0':'') + d.getMinutes(); var markup = chat.render('chatLine',params), exists = $('#chatLineHolder .chat-'+params.id); if(exists.length){ exists.remove(); } if(!chat.data.lastID){ // If this is the first chat, remove the // paragraph saying there aren't any: $('#chatLineHolder p').remove(); } // If this isn't a temporary chat: if(params.id.toString().charAt(0) != 't'){ var previous = $('#chatLineHolder .chat-'+(+params.id - 1)); if(previous.length){ previous.after(markup); } else chat.data.jspAPI.getContentPane().append(markup); } else chat.data.jspAPI.getContentPane().append(markup); // As we added new content, we need to // reinitialise the jScrollPane plugin: chat.data.jspAPI.reinitialise(); chat.data.jspAPI.scrollToBottom(true); }, The addChat() method takes a parameter object with the content of the chat, author and gravatar, and inserts the new chat line in the appropriate place in the #chatContainer div. Each chat (if not a temporary one) has a unique ID which is assigned by MySQL. This id is assigned as a class name to the chat in the form of chat-123.

When the addChat() method is run, it checks whether the previous chat entry exists (for chat-123 it would check chat-122). If it exists, it inserts the new chat after it. If it doesn’t, it just appends it to the div. This simple technique manages to insert all the chats in the right order and keep them that way.

Time in the User&#39;s Timezone


script.js – Part 5

// This method requests the latest chats // (since lastID), and adds them to the page. getChats : function(callback){ $.tzGET('getChats',{lastID: chat.data.lastID},function(r){ for(var i=0;i 3){ nextRequest = 2000; } if(chat.data.noActivity > 10){ nextRequest = 5000; } // 15 seconds if(chat.data.noActivity > 20){ nextRequest = 15000; } setTimeout(callback,nextRequest); }); }, // Requesting a list with all the users. getUsers : function(callback){ $.tzGET('getUsers',function(r){ var users = []; for(var i=0; i< r.users.length;i++){ if(r.users[i]){ users.push(chat.render('user',r.users[i])); } } var message = ''; if(r.total

موضوعات مشابه: