JavaScript >> Javascript 文檔 >  >> Tags >> CSS

製作 AJAX 網絡聊天(第 2 部分)- CSS 和 jQuery

在這個由兩部分組成的教程的第二部分中,我們將創建 AJAX 網絡聊天的 jQuery 和 CSS 前端。在第一部分,我們討論了 PHP 和 MySQL 方面。你可以在這裡閱讀第一部分。

現在讓我們從上次中斷的地方繼續。

CSS

聊天樣式是獨立的,並駐留在 chat.css .這些樣式獨立於頁面的其餘部分,因此更容易將聊天窗口嵌入到現有網站中。您只需要包含我們上週討論的 HTML 標記,並包含樣式表和 JavaScript 文件。

chat.css - 第 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;
}

我們從 #chatContainer 的樣式開始 分區。在自動邊距的幫助下,它在頁面上水平居中。正如您在教程的上一部分中看到的,這個 div 進一步分為頂部欄、聊天區、用戶區和底部欄。

頂部欄顯示用戶的登錄信息。它被分配了一個相對位置,以便頭像、名稱和註銷按鈕可以相應地定位。

之後是包含所有聊天的 div - #chatLineHolder .這個 div 具有固定的寬度和高度,正如您將在本教程的 jQuery 部分看到的那樣,我們使用 jScrollPane 插件將其變成帶有自定義側邊欄的精美可滾動區域。

chat.css - 第 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;
}

在第二部分中,我們為 #chatUsers 設置樣式 容器和用戶 div。每個活躍的聊天用戶都由一個 32 x 32 像素的頭像表示。默認定義為背景,當加載真實的背景圖像時,它們會顯示在它們的上方。這可以防止在加載圖像之前通常會發生的煩人的閃爍。

其餘代碼處理底部欄和提交表單。您可能會發現 .tip div 使用零高度和寬度以及大邊框值變成純 CSS 三角形的方式很有趣。我們在之前的教程中也使用過這個技巧。

chat.css - 第 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;
}

在代碼的最後一部分,我們覆蓋了 jScrollPane div 的默認樣式。默認情況下它顯示為紫色滾動條,這不太適合我們的設計。我們沒有從頭開始編寫自己的樣式表,而是只包含默認樣式表並覆蓋一些規則。

最後,您可以看到藍色按鈕的樣式。您可以將此類分配給任何常規錨點或按鈕,您將獲得一個漂亮的藍色按鈕。

jQuery

轉到本教程的最後一步 - jQuery 代碼。聊天的工作原理是監聽登錄和提交表單(以及註銷按鈕)上的事件,並將 AJAX 請求安排回服務器以檢查新的聊天和用戶。

正如您在上週教程的第一部分中看到的,在 PHP 端,AJAX 請求由 ajax.php 處理。 jQuery 發出多個 AJAX 請求:

  • 讓用戶登錄:這是通過單個 POST 請求完成的;
  • 註銷用戶:也是一個 POST 請求;
  • 檢查登錄用戶:每 15 秒檢查一次;
  • 檢查新聊天:每秒觸發一次 GET 請求。這可能意味著您的網絡服務器負載過重,這就是腳本在後端進行優化的原因,並且根據聊天的活動,請求會減少到每 15 秒一次。

正如您將在下面的代碼中看到的,我們為 jQuery 的 $.get 定義了自定義包裝器 和 $.post AJAX 函數,這將幫助我們不必填寫所有冗長的參數來發出請求。

此外,所有聊天代碼都組織在一個名為 chat 的對像中 .它由許多有用的方法組成,您將在下面的片段中看到。

script.js - 第 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;
        });

init() 的目的 方法是綁定聊天的所有事件處理程序並啟動用於安排新聊天和在線用戶檢查的超時功能。您可以看到我們使用了自己的包裝函數 - $.tzGET$.tzPOST .這些減輕了必須為 ajax 請求指定一長串參數和目標的負擔。

script.js - 第 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,'&lt;').replace(/>/g,'&gt;')
                };

            // 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);
        })();

    },

在腳本的第二部分,我們繼續綁定事件監聽器。在提交表單中,您可以看到當用戶添加新聊天時,會立即創建並顯示一個臨時聊天,而無需等待 AJAX 請求完成。寫入完成後,臨時聊天將從屏幕中刪除。這讓用戶感覺聊天快如閃電,而真正的寫入是在後台進行的。

在 init 方法接近尾聲時,我們運行了兩個自執行的命名函數。函數本身作為參數傳遞給各自的 chat.getChats() chat.getUsers() 方法,以便可以安排額外的超時(您可以在代碼的第 5 部分中看到這一點)。

script.js - 第 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 = [
                '<span><img src="',params.gravatar,'" width="23" height="23" />',
                '<span class="name">',params.name,
                '</span><a href="" class="logoutButton rounded">Logout</a></span>'];
            break;

            case 'chatLine':
                arr = [
                    '<div class="chat chat-',params.id,' rounded"><span class="gravatar">'+
                    '<img src="',params.gravatar,'" width="23" height="23" '+
                    'onload="this.style.visibility=\'visible\'" />',
                    '</span><span class="author">',params.author,
                    ':</span><span class="text">',params.text,
                    '</span><span class="time">',params.time,'</span></div>'];
            break;

            case 'user':
                arr = [
                    '<div class="user" title="',params.name,'"><img src="',params.gravatar,
                    '" width="30" height="30" onload="this.style.visibility=\'visible\'"'+
                    ' /></div>'
                ];
            break;
        }

        // A single array join is faster than
        // multiple concatenations

        return arr.join('');

    },

這裡是 render() 方法最值得我們關注。它的作用是根據傳遞的模板參數組裝一個模板 .然後該方法創建並返回請求的 HTML 代碼,並結合第二個參數的值 - params 對象 如所須。此處討論的大多數其他方法都使用此方法。

script.js - 第 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);

    },

addChat() 方法接受一個包含聊天內容、作者和頭像的參數對象,並將新的聊天行插入到 #chatContainer 中的適當位置 分區。每個聊天(如果不是臨時聊天)都有一個由 MySQL 分配的唯一 ID。此 id 以 chat-123 的形式作為類名分配給聊天 .

addChat() 方法運行時,它檢查上一個聊天條目是否存在(對於 chat-123 它會檢查 chat-122 )。如果存在,它會在其後插入新的聊天。如果沒有,它只是將其附加到 div。這種簡單的技術設法以正確的順序插入所有聊天並保持這種方式。

script.js - 第 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<r.chats.length;i++){
                chat.addChatLine(r.chats[i]);
            }

            if(r.chats.length){
                chat.data.noActivity = 0;
                chat.data.lastID = r.chats[i-1].id;
            }
            else{
                // If no chats were received, increment
                // the noActivity counter.

                chat.data.noActivity++;
            }

            if(!chat.data.lastID){
                chat.data.jspAPI.getContentPane().html('<p class="noChats">No chats yet</p>');
            }

            // Setting a timeout for the next request,
            // depending on the chat activity:

            var nextRequest = 1000;

            // 2 seconds
            if(chat.data.noActivity > 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<1){
                message = 'No one is online';
            }
            else {
                message = r.total+' '+(r.total == 1 ? 'person':'people')+' online';
            }

            users.push('<p class="count">'+message+'</p>');

            $('#chatUsers').html(users.join(''));

            setTimeout(callback,15000);
        });
    },

在這裡,我們負責管理 AJAX 請求。在 getChats() 方法,您可以看到我們正在根據本地數據對象的 noActivity 屬性來確定何時再次運行該函數。在每個不返回新聊天的請求上,我們都會增加計數器。如果達到某個閾值,則延遲下一個請求。

script.js - 第 6 部分

    // This method displays an error message on the top of the page:

    displayError : function(msg){
        var elem = $('<div>',{
            id        : 'chatErrorMessage',
            html    : msg
        });

        elem.click(function(){
            $(this).fadeOut(function(){
                $(this).remove();
            });
        });

        setTimeout(function(){
            elem.click();
        },5000);

        elem.hide().appendTo('body').slideDown();
    }
};

// Custom GET & POST wrappers:

$.tzPOST = function(action,data,callback){
    $.post('php/ajax.php?action='+action,data,callback,'json');
}

$.tzGET = function(action,data,callback){
    $.get('php/ajax.php?action='+action,data,callback,'json');
}

// A custom jQuery method for placeholder text:

$.fn.defaultText = function(value){

    var element = this.eq(0);
    element.data('defaultText',value);

    element.focus(function(){
        if(element.val() == value){
            element.val('').removeClass('defaultText');
        }
    }).blur(function(){
        if(element.val() == '' || element.val() == value){
            element.addClass('defaultText').val(value);
        }
    });

    return element.blur();
}

最後這些是我們的輔助方法和函數。 displayError() 如果發生錯誤,方法會在頁面頂部顯示友好的紅色條。在這之後是我們自定義的 $.tzGET 和 $.tzPOST 包裝器,最後是 defaultText 插件,我們不久前開發的用於顯示佔位符文本。

這樣我們的 AJAX 聊天就完成了!

總結一下<​​/h2>

在這兩個部分的教程中,我們使用 PHP 類、MySQL 數據庫和敏捷的 jQuery 前端開發了一個完整的聊天解決方案。您可以使用它為您的網站提供實時支持,或者讓您的網站訪問者玩得開心。這是中小型聊天(同時少於 20 人)的絕佳解決方案。

如果您喜歡本教程,請務必在 twitter 上關注我們以獲取有用的 Web 開發鏈接,並訂閱我們的 RSS 源。


Tutorial JavaScript 教程
  1. Kinsta Kingpin:採訪現代部落的 Travis Totz

  2. 在 Web 上檢測全選

  3. 300行重寫React Fiber核心算法

  4. 事件驅動架構

  5. 在 HTML5 和 JavaScript 中循環訪問 localStorage

  6. 無法將 JSON 解析為 Javascript 對象

  7. 使用 Javascript 和 CSS3 自定義鼠標指針

  1. 在 JavaScript 測試中模擬的提示和技巧

  2. 如何使用內置調試器調試 Node.js

  3. 嚐嚐 JavaScript 的新並行原語

  4. 編程之路

  5. 在javascript中將對像數組轉換為數組?

  6. React 的多範式狀態管理器約 2 kB。

  7. 如何在 TypeScript 中編寫函數

  1. 介紹 Node.js 包分析器

  2. React 中的動態表單字段

  3. Fugu 項目:在 PWA 中試用的 5 個新 API

  4. 構建以 UI/UX 為重點的產品組合