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

製作 Google Wave 歷史滑塊

簡介

我們都看過 Google 最新產品 Wave 的視頻(有些甚至可以訪問開發者的預覽版)。雖然不像我們想像的那樣“突破性”和“革命性”(想知道為什麼會想到“過度炒作”),但它仍然具有一些很棒的 UI,肯定會激發至少一些開發人員在他們的有效。

作為那些受到啟發的人之一,我將向您展示如何創建類似 Google Wave 的歷史滑塊。使用它,我們將使訪問者能夠及時來回查看評論線程上發生的更改。

所以看一下演示(甚至可以在單獨的選項卡中打開它),下載示例文件並繼續閱讀。

第 1 步 - XHTML

我們使用的技術包括 PHP 作為後端,MySQL 作為數據存儲, jQuery , CSSXHTML 對於帶有 AJAX 的前端 介於兩者之間。滑塊本身是一個使用 jQuery UI 創建的組件。

首先讓我們看一下 demo.php 的正文部分

demo.php

<div id="main">

<p id="orig">View the <a href="https://tutorialzine.com/2009/10/google-wave-history-slider-jquery/" target="_blank">original tutorial &raquo;</a></p>
<h1>Google Wave-like</h1>
<h2>History Slider</h2>

<div id="wave">
<div id="topBar">Your Demo Wave</div>
<div id="subBar">
<img src="img/tutorialzine.png" alt="Tutorialzine" /><img src="img/demo.png" alt="Demo" /><img src="img/curious.png" alt="Curious" />
</div>

<div id="sliderContainer">
<div id="slider"></div>
<div class="clear"></div>
</div>

<div id="commentArea">

<?php
foreach($comments as $c)
{
    showComment($c);
    // Showing each comment
}
?>

</div>
<input type="button" class="waveButtonMain" value="Add a comment" onclick="addComment()" />

<div id="bottomBar">
</div>

</div>
</div>

這幾乎是我們使用的所有佈局。代碼這麼短的主要原因是我們使用 CSS 來設置樣式,並且註釋的輸出是由一個特殊的 PHP 函數處理的,這兩者將在一分鐘內解釋。

第 2 步 - CSS

jQuery 的偉大之處在於,借助 Google 的 CDN,您可以直接將其包含在您的站點中,而不必擔心將其下載並存儲在您的服務器上。這也有助於縮短頁面加載時間。

jQuery UI 也是如此,它包含我們的滑塊。不僅如此,CDN 還保存了正確顯示所需的樣式和圖像。

但是,我們仍然必須包含我們自己的自定義樣式。在示例文件中,您可以在 demo.css 中找到它們 .這裡只顯示更有趣的部分:

demo.css

#orig{
    /* The link that float to the right of the title */
    float:right;
    font-family:"MyRiad Pro",Arial;
    font-size:10px;
    letter-spacing:1px;
    text-transform:uppercase;
    padding-top:10px;
}

.clear{
    /* Clearfix, needed by IE6 */
    clear:both;
}

#main{
    /* The main container */
    width:600px;
    margin:30px auto;
}

#wave{
    /* CSS rounded corners */
    -moz-border-radius:6px;
    -khtml-border-radius: 6px;
    -webkit-border-radius: 6px;
    border-radius:6px;

    background:white;
    width:100%;
    overflow:hidden;
}

#topBar{
    background:url(img/bg.jpg) repeat-x;
    font-size:12px;
    color:white;

    height:20px;
    overflow:hidden;
    padding:5px 0 0 10px;

    border-bottom:1px solid #e4f1ff;
    -moz-border-radius:6px 6px 0 0;
    /* A Firefox fix, for once */
}

#bottomBar{
    height:40px;
    background-color:#c9e2fc;
    -moz-border-radius:0 0 6px 6px;
    border-top:1px solid #CCCCCC;
}

#subBar{
    background-color:#c9e2fc;
    padding-left:10px;
}

#subBar img{
    /* The avatars at the top of the page */
    margin:8px 8px 8px 0;
    border:1px solid #cccccc;
}

.waveButton,.waveButtonMain{
    /* The submit buttons */
    background:url(img/button_bg.jpg) repeat-x 50% 50%;
    border:1px solid #DDDDDD;
    padding:4px;

    cursor:pointer;
}

.waveButtonMain{
    display:block;
    margin:10px 20px;
}

.textArea{
    padding:4px;
    font-family:Arial,Helvetica,Sans-serif;
    font-size:12px;
    color:#666666;
    border:1px solid #66aff9;
    margin-bottom:10px;
}

.replyLink{
    float:right;
}

#commentArea{
    padding:10px;
    color:#444444;
}

.commentText{
    margin-left:40px;
}

.waveComment .waveComment{
    padding-left:30px;
}

.waveComment .waveComment .replyLink{
    /* Hiding the reply link on the comment replies -
    only 2 levels of ancestry are allowed */

    display:none;
}

.waveTime{
    color:#999999;
    float:right;
    font-size:10px;
}

#slider{
    width:400px;
    font-size:10px;
    float:right;
    margin-right:10px;
}

#sliderContainer{
    background:url(img/dark_bg.jpg) repeat-x #f5f5f5 50% 50%;
    padding:9px 10px;
    border:1px solid #bbbbbb;
    border-left:0;
    border-right:0;

    height:10px;
    padding:9px 10px;
}

div.ui-widget-content{
    /* Styling the slider */
    background:#FFFFFF;
    border:1px solid #CCCCCC;
}

.comment{
    margin:5px 10px;
    padding:8px 10px;
    border:2px solid #cccccc;

    /* Rounding the comment */
    -moz-border-radius:6px;
    -khtml-border-radius: 6px;
    -webkit-border-radius: 6px;
    border-radius:6px;

    overflow:hidden;
}

span.name{
    font-weight:bold;
    color:#999999;
}

.commentAvatar{
    width:30px;
    height:30px;
    float:left;
    margin-right:10px;
}

第 3 步 - PHP

有四個主要的 PHP 文件處理後端:

  1. demo.php - 輸出評論;
  2. ajax/saveComment.php - 添加新評論,由 AJAX 請求訪問;
  3. functions.php - 保存了demo.php用到的一些函數;
  4. connect.php - 處理數據庫連接。

我們將在這裡只查看前三個文件。

demo.php

define("INCLUDE_CHECK",1);
require 'connect.php';
require 'functions.php';
// Including the files for the DB connection and our custom functions
// Removing comments that are older than an hour.

mysql_query("DELETE FROM wave_comments WHERE id>5 AND dt<SUBTIME(NOW(),'0 1:0:0')");

$comments_result = mysql_query("SELECT * FROM wave_comments ORDER BY id ASC");
// Selecting all the comments ordered by id in ascending order

$comments=array();
$js_history='';

while($row=mysql_fetch_assoc($comments_result))
{
    if($row['parent']==0)
    // If the comment is not a reply to a previous comment, put it into $comments directly
    $comments[$row['id']] = $row;
    else
    {
        if(!$comments[$row['parent']]) continue;

        $comments[$row['parent']]['replies'][] = $row;
        // If it is a reply, put it in the 'replies' property of its parent
    }

    $js_history.='addHistory({id:"'.$row['id'].'"});'.PHP_EOL;
    // Adds JS history for each comment
}

$js_history='<script type="text/javascript">
'.$js_history.'
</script>';

// This is later put into the head and executed on page load

評論是以太父母(它們直接添加到線程中)或孩子(作為對父母的回复添加)。只允許兩個級別的祖先(意味著對孩子禁用回复)。

評論稍後由 showComment 輸出 函數(您可以在上面的 XHTML 步驟中看到它)。

ajax/saveComment.php

define("INCLUDE_CHECK",1);
require'../connect.php';

if(empty($_POST['comment'])) die("0");
// If there isn't a comment text, exit

$comment = mysql_real_escape_string(nl2br(strip_tags($_POST['comment'])));
$user='Demo';
// This would be a nice place to start customizing - the default user
// You can integrate it to any site and show a different username.

$addon='';
if($_POST['parent']) $addon=',parent='.(int)$_POST['parent'];

mysql_query("INSERT INTO wave_comments SET usr='".$user."', comment='".$comment."', dt=NOW()".$addon);

if(mysql_affected_rows($link)==1)
    echo mysql_insert_id($link);
    // If the insert was successful, echo the newly assigned ID
else
    echo '0';

最後,還有 functions.php

functions.php

if(!defined('INCLUDE_CHECK')) die('You are not allowed to execute this file directly');

function showComment($arr)
{
    echo '
    <div class="waveComment com-'.$arr['id'].'">

        <div class="comment">
        <div class="waveTime">'.waveTime($arr['dt']).'</div>
        <div class="commentAvatar">
        <img src="img/'.strtolower($arr['usr']).'.png" width="30" height="30" alt="'.$arr['usr'].'" />
        </div>

        <div class="commentText">
        <span class="name">'.$arr['usr'].':</span> '.$arr['comment'].'
        </div>

        <div class="replyLink">
        <a href="" onclick="addComment(this,'.$arr['id'].');return false;">add a reply &raquo;</a>
        </div>

        <div class="clear"></div>
    </div>';

    // Output the comment, and its replies, if any
    if($arr['replies'])
    {
        foreach($arr['replies'] as $r)
        showComment($r);
    }
    echo '</div>';
}

function waveTime($t)
{
    $t = strtotime($t);

    if(date('d')==date('d',$t)) return date('h:i A',$t);
    return date('F jS Y h:i A',$t);
    // If the comment was written today, output only the hour and minute
    // if it was not, output a full date/time
}

最後一步是最棘手的——在本例中是 jQuery 代碼。

第 4 步 - jQuery

所有的 JS 代碼都位於 script.js .我將它分為兩部分:

script.js - 第 1 部分

$(document).ready(function(){
    // Executed once all the page elements are loaded

    lastVal = totHistory;

    // Create the slider:
    $("#slider").slider({
        value:totHistory,
        min: 1,
        max: totHistory,
        animate: true,
        slide: function(event, ui) {

            if(lastVal>ui.value)
            $(buildQ(lastVal,ui.value)).hide('fast').find('.addComment').remove();
            // Using buildQ to build the jQuery selector
            // If we are moving the slider backward, hide the previous comment

            else if(lastVal<ui.value)
            $(buildQ(lastVal,ui.value)).show('fast');
            // Otherwise show it

            lastVal = ui.value;
        }
    });
});

var totHistory=0;
// Holds the number of comments

var positions = new Array();
var lastVal;

function addHistory(obj)
{
    /* Gets called on page load for each comment, and on comment submit */
    totHistory++;
    positions.push(obj.id);
}

function buildQ(from,to)
{
    /* Building a jQuery selector from the begin
    and end point of the slide */

    if(from>to)
    {
        var tmp=to;
        to=from;
        from=tmp;
    }

    from++;
    to++;

    var query='';
    for(var i=from;i<to;i++)
    {
        if(i!=from) query+=',';
        query+='.com-'+positions[i-1];
    }

    /* Each comment has an unique com-(Comment ID) class
    that we are using to address it */

    return query;
}

您還記得,我們​​生成了一個特殊的 PHP 字符串,它保存對 addHistory 的調用 功能。每次運行時,它都會增加 totHistory 櫃檯。加載所有評論後 $(document).ready 運行並使用 totHistory 初始化滑塊 作為滑塊的最大值。最小值為 1,因為我們希望至少有一條評論可見。

現在讓我們看一下文件的第二部分。

script.js - 第 2 部分

function addComment(where,parent)
{
    /*  This functions gets called from both the "Add a comment" button
    on the bottom of the page, and the add a reply link.
    It shows the comment submition form */

    var $el;
    if($('.waveButton').length) return false;
    // If there already is a comment submition form
    // shown on the page, return and exit

    if(!where)
        $el = $('#commentArea');
    else
        $el = $(where).closest('.waveComment');

    if(!parent) parent=0;

    // If we are adding a comment, but there are hidden comments by the slider:
    $('.waveComment').show('slow');
    lastVal = totHistory;

    $('#slider').slider('option','value',totHistory);
    // Move the slider to the end point and show all comments
    var comment = '<div class="waveComment addComment">\
    \
    <div class="comment">\
    <div class="commentAvatar">\
    <img src="img/demo.png" width="30" height="30" />\
    </div>\
    \
    <div class="commentText">\
    \
    <textarea class="textArea" rows="2" cols="70" name="" />\
    <div><input type="button" class="waveButton" value="Add comment" onclick="addSubmit(this,'+parent+')" /> or <a href="" onclick="cancelAdd(this);return false">cancel</a></div>\
    \
    </div>\
    </div>\
    \
    </div>';

    $el.append(comment);
    // Append the form
}

function cancelAdd(el)
{
    $(el).closest('.waveComment').remove();
}

function addSubmit(el,parent)
{
    /* Executed when clicking the submit button */
    var cText = $(el).closest('.commentText');
    var text = cText.find('textarea').val();
    var wC = $(el).closest('.waveComment');
    if(text.length<4)
    {
        alert("Your comment is too short!");
        return false;
    }

    $(el).parent().html('<img src="img/ajax_load.gif" width="16" height="16" />');
    // Showing the loading gif animation
    // Send an AJAX request:

    $.ajax({
        type: "POST",
        url: "ajax/saveComment.php",
        data: "comment="+encodeURIComponent(text)+"&parent="+parent,
        /* Sending both the text and the parent of the comment */
        success: function(msg){

            /* PHP returns the automatically assigned ID of the new comment */
            var ins_id = parseInt(msg);
            if(ins_id)
            {
                wC.addClass('com-'+ins_id);
                addHistory({id:ins_id});
                $('#slider').slider('option', 'max', totHistory).slider('option','value',totHistory);
                lastVal=totHistory;
            }

            transForm(text,cText);
            // Hiding the form and showing the newly-added comment in its place
        }
    });
}

function transForm(text,cText)
{
    var tmpStr ='<span class="name">Demo:</span> '+text;
    cText.html(tmpStr);
}

這部分代碼中的函數通過AJAX處理評論提交 到 PHP 後端。

我認為成功需要進一步澄清 AJAX 中的函數。如您所知,當我們成功執行 AJAX 請求時調用它(在這種情況下,如果將註釋寫入 MySQL 數據庫,則會調用它)。

在這個函數中,我們檢查是否返回了正確的插入 ID,它對應於提供給 auto-increment 的內部 MySQL id 字段(請參閱下面的 MySQL 部分或查看 table.sql 在示例文件中)。

如果一切正常,我們調用 addHistory 使用新數據運行並更新滑塊的最大值。這確保了新添加的評論可以與其他評論一起歷史滾動。

第 5 步 - MySQL

僅當您想在自己的服務器上運行演示時才需要此步驟。如果您遇到問題,請仔細閱讀下面的評論,如果您的問題沒有解決,請寫一個新的。

為了能夠運行演示,您必須創建 MySQL 表 wave_comments 使用以下代碼(也可在 table.sql 中找到 在示例文件中):

CREATE TABLE `wave_comments` (
`id` int(11) NOT NULL auto_increment,
`parent` int(11) NOT NULL default '0',
`usr` varchar(16) collate utf8_unicode_ci NOT NULL default '',
`comment` text collate utf8_unicode_ci NOT NULL,
`dt` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY  (`id`),
KEY `parent` (`parent`,`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

有了這個,我們的類似 Google Wave 的歷史滑塊就完成了!

結論

您可以自由修改此示例的代碼並在您自己的站點中使用它。此外,如果您通過我們新添加的功能 - Tutorial Mashups 與社區分享您所做的事情,那就太好了 (您可以在每個教程下方找到它)。


Tutorial JavaScript 教程
  1. 像我五歲一樣解釋事件冒泡

  2. JavaScript 按字母順序排序對像數組 |示例代碼

  3. 如何:使用 Select 響應表

  4. 什麼是 Node.js,它是如何工作的?

  5. JavaScript 桌面自動化

  6. 使用 Go 和 Vue.js 構建實時評論源

  7. JavaScript 即將推出兩個很酷的功能

  1. JavaScript 中是否有睡眠/暫停/等待功能?

  2. 使用 Formik 和 React 構建表單 - 第 2 部分

  3. Microsoft Learn 上的免費 Node.js 課程!

  4. 讓您的 Angular SPA 可訪問

  5. Line Clampin(截斷多行文本) - 更智能的方式

  6. Vue.js 3:第一步

  7. 在沒有 react-native 鏈接的情況下安裝 react-native-sentry

  1. 如何在 JavaScript 中查找字符的 ASCII 值

  2. 在 JavaScript 中組合字符串的 4 種方法

  3. 異步 JavaScript:回調、承諾、異步/等待、事件循環 - 第 2 部分

  4. 在 Node JS 中設置 Jasmine 測試框架分步指南