Tutorial: Making a Shoutbox with PHP and jQuery

Tutorial: Making a Shoutbox with PHP and jQuery

In this tutorial, we are going to build a shout box with PHP and jQuery, which allows visitors of your website to leave short comments to one another. Shouts will be stored on the server as files, no database like MySQL will be required. We are going to use two PHP libraries to make things easier – Flywheel for storing the shouts as json files and RelativeTime for creating human readable relative time stamps. We will be using Composer to install these libraries.

On the client side, we are using plain jQuery code, and the Emoji One library, which is a free project and library for adding pretty emojis to web apps. Let’s begin!

Running the shoutbox

You can grab the source code from the download button above. It has plenty of comments and is easy to follow. To run it, simply upload it to your web hosting space or add it to the apache htdocs folder if you run something like XAMPP or MAMP. Then, open http://localhost in your browser (or your website, if you uploaded it to your hosting space). Here are a few things to look for:

  • The zip files already contains the dependencies, so you don’t need to install Composer. This makes it easy to get started with the code – simply upload it and use it!
  • Make sure that the data/shouts directory exists and is writable. Otherwise you will see errors in your log file and no shouts will be stored. You might need to chmod it to 777 if you keep seeing errors.

The HTML

Let’s start with index.html. It is a regular HTML5 document, which includes our JavaScript libraries, scripts and stylesheets. Here are the parts relevant to the shoutbox:

index.html

<div class="shoutbox">
    
    <h1>Shout box <img src='./assets/img/refresh.png'/></h1>
    
    <ul class="shoutbox-content"></ul>
    
    <div class="shoutbox-form">
        <h2>Write a message <span>×</span></h2>
        
        <form action="./publish.php" method="post">
            <label for="shoutbox-name">nickname </label> <input type="text" id="shoutbox-name" name="name"/>
            <label class="shoutbox-comment-label" for="shoutbox-comment">message </label> <textarea id="shoutbox-comment" name="comment" maxlength='240'></textarea>
            <input type="submit" value="Shout!"/>
        </form>
    </div>
    
</div>

With JavaScript we will insert the published shouts into the <ul> element. The form is hidden by default, and only revealed when the “Write a message” header is clicked.

Shoutbox with PHP and jQuery

Shoutbox with PHP and jQuery

The JavaScript Code

And here is our script.js, which makes the above HTML work:

assets/js/script.js

$(function(){

    // Storing some elements in variables for a cleaner code base

    var refreshButton = $('h1 img'),
        shoutboxForm = $('.shoutbox-form'),
        form = shoutboxForm.find('form'),
        closeForm = shoutboxForm.find('h2 span'),
        nameElement = form.find('#shoutbox-name'),
        commentElement = form.find('#shoutbox-comment'),
        ul = $('ul.shoutbox-content');

    // Replace :) with emoji icons:
    emojione.ascii = true;

    // Load the comments.
    load();
    
    // On form submit, if everything is filled in, publish the shout to the database
    
    var canPostComment = true;

    form.submit(function(e){
        e.preventDefault();

        if(!canPostComment) return;
        
        var name = nameElement.val().trim();
        var comment = commentElement.val().trim();

        if(name.length && comment.length && comment.length < 240) {
        
            publish(name, comment);

            // Prevent new shouts from being published

            canPostComment = false;

            // Allow a new comment to be posted after 5 seconds

            setTimeout(function(){
                canPostComment = true;
            }, 5000);

        }

    });
    
    // Toggle the visibility of the form.
    
    shoutboxForm.on('click', 'h2', function(e){
        
        if(form.is(':visible')) {
            formClose();
        }
        else {
            formOpen();
        }
        
    });
    
    // Clicking on the REPLY button writes the name of the person you want to reply to into the textbox.
    
    ul.on('click', '.shoutbox-comment-reply', function(e){
        
        var replyName = $(this).data('name');
        
        formOpen();
        commentElement.val('@'+replyName+' ').focus();

    });
    
    // Clicking the refresh button will force the load function
    
    var canReload = true;

    refreshButton.click(function(){

        if(!canReload) return false;
        
        load();
        canReload = false;

        // Allow additional reloads after 2 seconds
        setTimeout(function(){
            canReload = true;
        }, 2000);
    });

    // Automatically refresh the shouts every 20 seconds
    setInterval(load,20000);


    function formOpen(){
        
        if(form.is(':visible')) return;

        form.slideDown();
        closeForm.fadeIn();
    }

    function formClose(){

        if(!form.is(':visible')) return;

        form.slideUp();
        closeForm.fadeOut();
    }

    // Store the shout in the database
    
    function publish(name,comment){
    
        $.post('publish.php', {name: name, comment: comment}, function(){
            nameElement.val("");
            commentElement.val("");
            load();
        });

    }
    
    // Fetch the latest shouts
    
    function load(){
        $.getJSON('./load.php', function(data) {
            appendComments(data);
        });
    }
    
    // Render an array of shouts as HTML
    
    function appendComments(data) {

        ul.empty();

        data.forEach(function(d){
            ul.append('<li>'+
                '<span class="shoutbox-username">' + d.name + '</span>'+
                '<p class="shoutbox-comment">' + emojione.toImage(d.text) + '</p>'+
                '<div class="shoutbox-comment-details"><span class="shoutbox-comment-reply" data-name="' + d.name + '">REPLY</span>'+
                '<span class="shoutbox-comment-ago">' + d.timeAgo + '</span></div>'+
            '</li>');
        });

    }

});

The Emoji One library has version for both JavaScript and PHP. In the appendComments method, we use the emojione.toImage() function to convert all typed-out smileys into emoji. See all functions that are supported, and check out this handy emoji code website. Now that the frontend is ready, let’s move on to the backend.

The PHP Code

We have two files – publish.php and load.php. The first accepts a POST request for storing shouts in the data store, and the second returns the 20 latest shouts. These files are not opened directly by visitors – they only handle AJAX requests.

publish.php

<?php

// Include our composer libraries
require 'vendor/autoload.php';

// Configure the data store

$dir = __DIR__.'/data';

$config = new \JamesMoss\Flywheel\Config($dir, array(
    'formatter' => new \JamesMoss\Flywheel\Formatter\JSON,
));

$repo = new \JamesMoss\Flywheel\Repository('shouts', $config);
    
// Store the posted shout data to the data store

if(isset($_POST["name"]) && isset($_POST["comment"])) {
    
    $name = htmlspecialchars($_POST["name"]);
    $name = str_replace(array("\n", "\r"), '', $name);

    $comment = htmlspecialchars($_POST["comment"]);
    $comment = str_replace(array("\n", "\r"), '', $comment);
    
    // Storing a new shout

    $shout = new \JamesMoss\Flywheel\Document(array(
        'text' => $comment,
        'name' => $name,
        'createdAt' => time()
    ));
    
    $repo->store($shout);
    
}

Here we directly use the Flywheel library we mentioned in the beginning. Once it is configured, you can store any type of data, which will be written as a JSON file in the data/shouts folder. Reading these shouts is done in load.php:

load.php

<?php

require 'vendor/autoload.php';

// If you want to delete old comments, make this true. We use it to clean up the demo.
$deleteOldComments = false;

// Setting up the data store

$dir = __DIR__.'/data';

$config = new \JamesMoss\Flywheel\Config($dir, array(
    'formatter' => new \JamesMoss\Flywheel\Formatter\JSON,
));

$repo = new \JamesMoss\Flywheel\Repository('shouts', $config);

// Delete comments which are more than 1 hour old if the variable is set to be true.

if($deleteOldComments) {
    
    $oldShouts = $repo->query()
                ->where('createdAt', '<', strtotime('-1 hour'))
                ->execute();

    foreach($oldShouts as $old) {
        $repo->delete($old->id);
    }
    
}

// Send the 20 latest shouts as json

$shouts = $repo->query()
        ->orderBy('createdAt ASC')
        ->limit(20,0)
        ->execute();

$results = array();

$config = array(
    'language' => '\RelativeTime\Languages\English',
    'separator' => ', ',
    'suffix' => true,
    'truncate' => 1,
);

$relativeTime = new \RelativeTime\RelativeTime($config);
        
foreach($shouts as $shout) {
    $shout->timeAgo = $relativeTime->timeAgo($shout->createdAt);
    $results[] = $shout;
}

header('Content-type: application/json');
echo json_encode($results);

We’ve included code which deletes shouts older than an hour. We use this feature to keep the demo clean. You can enable it if you wish. After selecting the shouts, we are also calculating the human-readable relative time stamp with the RelativeTime library.

With this our shoutbox is ready! You can embed it in your website, customize it and change the code any way you please. We hope you like it!

Presenting Bootstrap Studio

a revolutionary tool that developers and designers use to create
beautiful interfaces using the Bootstrap Framework.

Learn more
Web Browser Frame DevKit Box Mouse Cursor
by Nick Anastasov

Nick is a JavaScript programmer who loves Node and all things HTML5. He is interested in photography and is the resident food expert at Tutorialzine's office.

1Share this post
2Read one more article
3Get your free book
Book Cover
jQuery Trickshots

Tutorialzine's advanced jQuery techniques book.

Download

14 Comments

  1. Richard says:

    Really nice, thank you! :)

  2. Mitsuru says:

    Awesome!! a db-less mini-chat!!! thanks for share!!! ;)

  3. Volkan says:

    I did everything but it doesnt work.

    Take a look: http://aaa.antalyabalikcilik.com.tr/

    1. Martin Angelov says:

      In Chrome's Developer Tools, it shows that your PHP script is throwing a 500 error. Do you have access to your PHP error logs? You should find an error message there. Contact your web host if you can't access your logs.

      1. Alessio Alessandro Web says:

        Hi, you must set in ( load.php ) the default time zone!
        ex: " date_default_timezone_set('Europe/Rome'); "
        copy this code and will be work! ;) trust!

        1. Alessio says:

          How did you change the date_default_timezone?

  4. Satheeshkumar says:

    Wonderful plugin. Nice :)

  5. Steven says:

    Do you have a way to add the ability to upload a photo?

  6. Max says:

    How I can delete messages, please give me a hint.
    1. page for admin
    2. each post have link "delete"
    I need id field for every post to make requests or what?
    Looking for easy solution :)

  7. Good write-up. I absolutely appreciate this site. Keep it up!

  8. Dave says:

    Tnx for this app i also have problem when i deploy it on my website, it works on localhost... but in my site it does not work.

  9. Frank says:

    I set the timezone, but wont work in IE 11 an Edge. No refresh after submit.

    1. Frank says:

      This work:

      insert the following at the end of the script.js

      $(document).ready(function()
      {
      $.ajaxSetup ({
      // Disable caching of AJAX responses */
      cache: false
      });
      });

  10. Stephanie Hallberg says:

    We’ve included code which deletes shouts older than an hour. We use this feature to keep the demo clean. You can enable it if you wish.

    "So how ti NOT delete within one hour. and what code u mean? :)

    btw. the shoutbox looks awesome. so small, clean and customizable =)

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links