AJAX-enabled Sticky Notes With PHP & jQuery

Download

Today we are making an AJAX-enabled Sticky Note management system. It will give visitors the ability to create notes with a live preview, and move them around on the screen. Every movement is going to be sent to the back-end via AJAX and saved in the database.

We are using version 1.4 of jQuery and the fancybox plugin (you can also check our CSS3 Lightbox Gallery Tutorial, where we also used fancybox).

You can download the example files and keep the demo site open in a tab so that it is easier to follow the steps of the tutorial.

Note: This tutorial is quite old and doesn't work in PHP7 and above. We are keeping it online only as a reference.

Step 1 - XHTML

The first step is to create the necessary XHTML structure. The markup in the main demonstration file - demo.php is pretty straightforward, as you can see for yourself from the code below.

demo.php

<div id="main">
<a id="addButton" class="green-button" href="add_note.html">Add a note</a>

<?php echo $notes?>

</div>

It contains the main div, which holds all the notes and limits their movement during the dragging process. The rest is generated dynamically by PHP after running a SELECT query against the database, and after storing the result in the $notes variable as you will see in the next step.

If you click the "Add a note" button on the demonstration site, you will see that a form with a live preview pops up. This functionality is provided by the fancybox, which fetches add_note.html (which contains the form) and shows it within a pop-up.

add_note.html

<h3 class="popupTitle">Add a new note</h3>

<!-- The preview: -->
<div id="previewNote" class="note yellow" style="left:0;top:65px;z-index:1">
<div class="body"></div>
<div class="author"></div>
<span class="data"></span>
</div>

<div id="noteData"> <!-- Holds the form -->
<form action="" method="post" class="note-form">

<label for="note-body">Text of the note</label>
<textarea name="note-body" id="note-body" class="pr-body" cols="30" rows="6"></textarea>

<label for="note-name">Your name</label>
<input type="text" name="note-name" id="note-name" class="pr-author" value="" />

<label>Color</label> <!-- Clicking one of the divs changes the color of the preview -->
<div class="color yellow"></div>
<div class="color blue"></div>
<div class="color green"></div>

<!-- The green submit button: -->
<a id="note-submit" href="" class="green-button">Submit</a>

</form>
</div>

In the form you can fill in the text of the note, your name and choose a color. Providing a way for users to see how their note will appear in the page in real time is a useful addition which serves another practical purpose - when clicking the submit button and the lightbox window closes, the preview note is copied to the main div, which saves us from writing additional code.

i11.png

Step 2 - PHP

As mentioned earlier, PHP fills the $notes variable by running a query againts the database and outputs it on the page. Lets see how this works.

demo.php

$query = mysql_query("SELECT * FROM notes ORDER BY id DESC");

$notes = '';
$left='';
$top='';
$zindex='';
while($row=mysql_fetch_assoc($query))
{
    // The xyz column holds the position and z-index in the form 200x100x10:
    list($left,$top,$zindex) = explode('x',$row['xyz']);
    $notes.= '
        <div class="note '.$row['color'].'" style="left:'.$left.'px;top:'.$top.'px;  z-index:'.$zindex.'">
        '.htmlspecialchars($row['text']).'
        <div class="author">'.htmlspecialchars($row['name']).'</div>
        <span class="data">'.$row['id'].'</span>
    </div>';
}

The notes table not only stores the text and the author of the note, but it also has a dedicated column for the x and y coordinates, as well for the z-index (or stacking order). Those are stored in the xyz field of the table, which is updated by AJAX.

After the visitor clicks on the "Add a note" button, fancybox grabs add_note.html (which was covered in step one) and displays the live preview form. When it is submitted, the data is sent via AJAX to post.php, given below.

post.php

// Escaping the input data:
$author = mysql_real_escape_string(strip_tags($_POST['author']));
$body = mysql_real_escape_string(strip_tags($_POST['body']));
$color = mysql_real_escape_string($_POST['color']);
$zindex = (int)$_POST['zindex'];

/* Inserting a new record in the notes DB: */
mysql_query('   INSERT INTO notes (text,name,color,xyz)
VALUES ("'.$body.'","'.$author.'","'.$color.'","0x0x'.$zindex.'")');

if(mysql_affected_rows($link)==1)
{
    // Return the id of the inserted row:
    echo mysql_insert_id($link);
}
else echo '0';

After escaping all the input data and inserting it in the table, the script checks whether there were any rows affected after the insert query. If mysql_affected_rows returns 1, this would mean that the insert was successful and the automatically assigned auto_increment ID is outputted.

AJAX is also used to save the positions of the individual notes after the end of each movement. The JavaScript code that actually requests these AJAX calls is presented in step 4 of this tutorial. The PHP code is included below:

update_position.php

// Validating the input data:
if(!is_numeric($_GET['id']) || !is_numeric($_GET['x']) || !is_numeric($_GET['y']) || !is_numeric($_GET['z']))
die("0");

// Escaping:
$id = (int)$_GET['id'];
$x = (int)$_GET['x'];
$y = (int)$_GET['y'];
$z = (int)$_GET['z'];

// Saving the position and z-index of the note:
mysql_query("UPDATE notes SET xyz='".$x."x".$y."x".$z."' WHERE id=".$id);

echo "1";

After making sure that the input data is valid, the script updates the xyz field of the row for the note in question and prints 1 on success.

Lets continue with step three.

Step 3 - CSS

All the markup is in place, so it is time to throw in some fancy styling. The styles are defined in styles.css. I divided the file in three parts.

styles.css - Part 1

.note{
    height:150px;
    padding:10px;
    width:150px;
    position:absolute;
    overflow:hidden;
    cursor:move;

    font-family:Trebuchet MS,Tahoma,Myriad Pro,Arial,Verdana,sans-serif;
    font-size:22px;

    /* Adding a CSS3 shadow below the note, in the browsers which support it: */
    -moz-box-shadow:2px 2px 0 #DDDDDD;
    -webkit-box-shadow:2px 2px 0 #DDDDDD;
    box-shadow:2px 2px 0 #DDDDDD;
}

#fancy_ajax .note{ cursor:default; }

/* Three styles for the notes: */

.yellow{
    background-color:#FDFB8C;
    border:1px solid #DEDC65;
}

.blue{
    background-color:#A6E3FC;
    border:1px solid #75C5E7;
}

.green{
    background-color:#A5F88B;
    border:1px solid #98E775;
}

/* Each note has a data span, which holds its ID */
span.data{ display:none; }

/* The "Add a note" button: */
#addButton{
    position:absolute;
    top:-70px;
    left:0;
}

In the first part we define the appearance of the notes and provide three color schemes - yellow, blue and green. Those color classes are also used in the live preview form when creating a note.

Every note has special span element with a class name of data, which holds the internal ID it is assigned in the database. This ID is used by jQuery and returned with the AJAX calls back to the server in order to update the note's position and z-index. We are hiding this span from view with display:none.

i22.png

styles.css - Part 2

/* Green button class: */
a.green-button,a.green-button:visited{
    color:black;
    display:block;
    font-size:10px;
    font-weight:bold;
    height:15px;
    padding:6px 5px 4px;
    text-align:center;
    width:60px;

    text-shadow:1px 1px 1px #DDDDDD;
    background:url(img/button_green.png) no-repeat left top;
}

a.green-button:hover{
    text-decoration:none;
    background-position:left bottom;
}

.author{
    /* The author name on the note: */
    bottom:10px;
    color:#666666;
    font-family:Arial,Verdana,sans-serif;
    font-size:12px;
    position:absolute;
    right:10px;
}

#main{
    /* Contains all the notes and limits their movement: */
    margin:0 auto;
    position:relative;
    width:980px;
    height:500px;
    z-index:10;
    background:url(img/add_a_note_help.gif) no-repeat left top;
}

The second part of the file defines the green button class, complete with a hover state and a CSS3 text-shadow. These are a few tiny details that may not look like much, but leave a good overall impression to the user.

styles.css - Part 3

h3.popupTitle{
    border-bottom:1px solid #DDDDDD;
    color:#666666;
    font-size:24px;
    font-weight:normal;
    padding:0 0 5px;
}

#noteData{
    /* The input form in the pop-up: */
    height:200px;
    margin:30px 0 0 200px;
    width:350px;
}

.note-form label{
    display:block;
    font-size:10px;
    font-weight:bold;
    letter-spacing:1px;
    text-transform:uppercase;
    padding-bottom:3px;
}

.note-form textarea, .note-form input[type=text]{
    background-color:#FCFCFC;
    border:1px solid #AAAAAA;
    font-family:Arial,Verdana,sans-serif;
    font-size:16px;
    height:60px;
    padding:5px;
    width:300px;
    margin-bottom:10px;
}

.note-form input[type=text]{    height:auto; }

.color{
    /* The color swatches in the form: */
    cursor:pointer;
    float:left;
    height:10px;
    margin:0 5px 0 0;
    width:10px;
}

#note-submit{   margin:20px auto; }

In the last part of styles.css, we add CSS rules for the live preview form, staring from the H3 heading and finishing with the color swatches on the bottom.

Step 4 - jQuery

jQuery manages the front-end and all of the AJAX requests. In order to be able to use the library, we first need to include a few lines to the head section of demo.php:

demo.php

<link rel="stylesheet" type="text/css" href="styles.css" />
<link rel="stylesheet" type="text/css" href="fancybox/jquery.fancybox-1.2.6.css" media="screen" />

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="fancybox/jquery.fancybox-1.2.6.pack.js"></script>

<script type="text/javascript" src="script.js"></script>

We include jQuery and jQuery UI from Google's content repository network, as well as the rest of the css and js files needed for this tutorial. Now lets dig a bit deeper into our jQuery script.

script.js - Part 1

$(document).ready(function(){
    /* This code is executed after the DOM has been completely loaded */

    var tmp;

    $('.note').each(function(){
        /* Finding the biggest z-index value of the notes */

        tmp = $(this).css('z-index');
        if(tmp>zIndex) zIndex = tmp;
    })

    /* A helper function for converting a set of elements to draggables: */
    make_draggable($('.note'));

    /* Configuring the fancybox plugin for the "Add a note" button: */
    $("#addButton").fancybox({
        'zoomSpeedIn'       : 600,
        'zoomSpeedOut'      : 500,
        'easingIn'          : 'easeOutBack',
        'easingOut'         : 'easeInBack',
        'hideOnContentClick': false,
        'padding'           : 15
    });

    /* Listening for keyup events on fields of the "Add a note" form: */
    $('.pr-body,.pr-author').live('keyup',function(e){

        if(!this.preview)
            this.preview=$('#previewNote');

        /* Setting the text of the preview to the contents of the input field, and stripping all the HTML tags: */
        this.preview.find($(this).attr('class').replace('pr-','.')).html($(this).val().replace(/<[^>]+>/ig,''));
    });

    /* Changing the color of the preview note: */
    $('.color').live('click',function(){

        $('#previewNote').removeClass('yellow green blue').addClass($(this).attr('class').replace('color',''));
    });

First the script finds the maximum z-index value, so that it can cache it into the zIndex variable and increment it before assigning it to the notes in the beginning of every drag movement. This way, when you start dragging a note, it is moved to the top of the stack.

Another interesting moment is that we use the jQuery live() method to listen for events, instead of the regular bind(). This is so, because the page elements we are listening for events on, are created only when the form is shown and, once defined, live() event listeners are active on all elements yet to be created.

script.js - Part 2

  /* The submit button: */
    $('#note-submit').live('click',function(e){

        if($('.pr-body').val().length<4)
        {
            alert("The note text is too short!")
            return false;
        }

        if($('.pr-author').val().length<1)
        {
            alert("You haven't entered your name!")
            return false;
        }

        $(this).replaceWith('<img src="img/ajax_load.gif" style="margin:30px auto;display:block" />');

        var data = {
            'zindex'    : ++zIndex,
            'body'      : $('.pr-body').val(),
            'author'        : $('.pr-author').val(),
            'color'     : $.trim($('#previewNote').attr('class').replace('note',''))
        };

        /* Sending an AJAX POST request: */
        $.post('ajax/post.php',data,function(msg){

            if(parseInt(msg))
            {
                /* msg contains the ID of the note, assigned by MySQL's auto increment: */
                var tmp = $('#previewNote').clone();

                tmp.find('span.data').text(msg).end().css({'z-index':zIndex,top:0,left:0});
                tmp.appendTo($('#main'));

                make_draggable(tmp)
            }

            $("#addButton").fancybox.close();
        });

        e.preventDefault();
    })
});

Here we are listening for the click event on the form submit link. Once clicked, the data is validated and sent with the $.post method. If the insert was successful, the lightbox is hidden and the newly created note is added to the page. Notice that we are using the make_draggable function, which is included below.

i31.png

script.js - Part 3

var zIndex = 0;

function make_draggable(elements)
{
    /* Elements is a jquery object: */
    elements.draggable({
        containment:'parent',
        start:function(e,ui){ ui.helper.css('z-index',++zIndex); },
        stop:function(e,ui){

            /* Sending the z-index and positon of the note to update_position.php via AJAX GET: */
            $.get('ajax/update_position.php',{
                x       : ui.position.left,
                y       : ui.position.top,
                z       : zIndex,
                id  : parseInt(ui.helper.find('span.data').html())
            });
        }
    });
}

In the last part of script.js, we have the make_draggable function. It turns a set of jQuery elements passed as a parameter into draggable objects. I've moved this functionality into a separate function because we need to create draggables in twice - once on initial page load, and once when we add a new note to the page.

Step 5 - MySQL

If you plan on running and building upon this demo, you'll need to set up a working version on your server. You'll need to execute the code from table.sql of the download archive in phpMyAdmin and fill in your database credentials in connect.php.

With this our AJAX-ed Sticky Note system is complete!

Conclusion

Today we created an Ajax Enabled Sticky Note Management System and demonstrated how we could use one of the readily available plug-ins for the jQuery library to build a dynamic interface, complete with a live preview.

What do you think? How would you improve this code?

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles