Tag Archiv: php
As a web developer, you have to bring creativity and attention to detail to your work. It is often the little things that leave a lasting impression to a visitor. Be it a lovable character illustration, or unique slideshow, it is usually not the information you present that is memorable, but the way you present it.
Today we are making a complete jQuery & CSS website for a fictional mobile application. It is going to feature semantic markup and a progressively enhanced slideshow effect. It will allow the user to see four of the most popular smartphones running the mobile app.
Step 1 – XHTML
When building websites it is important that you lay your code in a semantic way. This would include using tags for what they were supposed to be used for. You should use headings for titles, paragraphs for text (instead of generic divs) and lists where applicable.
mobileapp.html
<div id="page">
<h1 id="logoh1"><a href="/" id="logo">MobileApp - the most useful mobile app!</a></h1>
<div id="phoneCarousel">
<div class="previous arrow"></div>
<div class="next arrow"></div>
<div id="stage">
<img id="iphone" class="default" src="img/phones/iphone.png" width="270" height="400" alt="iPhone" />
<img id="nexus" src="img/phones/nexus_one.png" width="270" height="400" alt="Nexus One" />
<img id="nokia" src="img/phones/nokia.png" width="270" height="400" alt="Nokia" />
<img id="blackberry" src="img/phones/blackberry.png" width="270" height="400" alt="BlackBerry" />
</div>
</div>
<img class="availableAppStore" src="img/available_on_the_appstore.png" width="230" height="80" alt="Available on the Appstore" />
<div class="text">
<h3><img src="img/thumb.png" alt="MobileApp" width="114" height="114" class="thumb" />A wonderful app</h3>
<p>Lorem ipsum dolor sit amet.. </p>
</div>
<div class="text">
<h3><img src="img/thumb.png" alt="MobileApp" width="114" height="114" class="thumb" />More awesome facts</h3>
<p>Lorem ipsum dolor sit amet.. </p>
</div>
</div>
This is all the markup that is used to display the website. The h1 heading holds a hyperlink which is styled as the logo (the logo image is set as the background of the hyperlink and a negative text indent is used to hide the text of the link).
After this we have the #phoneCarousel div and inside it are the arrows and the stage. The phone images inside the stage are rotated with jQuery as you will see in a moment.
Lastly we have the Available on the Appstore badge, and two blocks of text.
Slick MobileApp Website - Slideshow
Step 2 – CSS
CSS is responsible for converting our semantic markup into a true website. Take a closer look at the #stage styles in the second part of the code, as these are what make the animation possible.
styles.css – Part 1
body{
font-size:14px;
color:#515151;
background:url('img/bg.png') repeat-x #f6f8f9;
font-family:'Myriad Pro',Arial, Helvetica, sans-serif;
}
#logoh1{ margin:40px 0 0;}
#page{
/* This the main container div */
width:1000px;
min-height:700px;
margin:0 auto;
background:url('img/bokeh.jpg') no-repeat 0 120px;
position:relative;
padding-top:1px;
}
#phoneCarousel{
/* This is the carousel section, it
contains the stage and the arrows */
height:390px;
margin:90px auto 120px;
position:relative;
width:800px;
}
#phoneCarousel .arrow{
/* The two arrows */
width:44px;
height:44px;
background:url('img/arrows.png') no-repeat;
position:absolute;
top:50%;
margin-top:-22px;
left:0;
cursor:pointer;
}
#phoneCarousel .next{
/* Individual styles for the next icon */
background-position:right top;
left:auto;
right:0;
}
/* Hover styles */
#phoneCarousel .arrow:hover{
background-position:left bottom;
}
#phoneCarousel .next:hover{
background-position:right bottom;
}
After defining the body styles we can move on to styling the #page div, which holds everything together. Its background image is offset 120px vertically, so it matches the background of the body, filling the full width of the page.
Next is the #phoneCarousel div. It has a relative positioning applied, so the stage (where all the animations take place) can be properly centered. The previous/next arrows are styled as well.
styles.css – Part 2
#logo{
background:url('img/logo.png') no-repeat;
height:40px;
text-indent:-9999px;
width:210px;
display:block;
}
#stage{
/* The stage contains the animated phone images */
left:50%;
margin-left:-350px;
position:absolute;
width:700px;
height:100%;
}
#stage img{
/* Hiding all the images by default */
display:none;
}
#stage .default{
/* This class is applied only to the iphone img by default
and it is the only one visible if JS is disabled */
display:block;
left:50%;
margin-left:-135px;
position:absolute;
}
#stage .animationReady{
/* This class is assigned to the images on load */
display:block;
position:absolute;
top:0;
left:0;
}
.text{ margin-top:70px;width:700px;}
.text p,
.text h3{
padding-bottom:15px;
line-height:1.4;
text-align:justify;
}
.text h3{ font-size:30px;}
.text p{ font-size:20px;}
.thumb{ float:left;margin-right:40px;}
.availableAppStore{float:right;}
In the second part of the stylesheet, we continue with the #stage styles. The phone pictures are hidden by default, so if JavaScript is disabled, the user is not left with a bunch of scattered images.
As you will see in the next step, the animation is achieved through changing the top and left CSS properties. For this to work, the images must be absolutely positioned. This is why the .animatonReady class is assigned on load with jQuery (if JS is disabled, this style would not be applied).
Lastly we style the text blocks, which explain details about our fictional MobileApp.
Text Blocks
Step 3 – jQuery
When you click on one of the arrows, an animation starts, which uses sine and cosine calculations to move and scale down the images, creating the illusion of a circular movement. It is not as complicated as it sounds, as you can see for yourself from the code below.
script.js
$(document).ready(function(){
var deg=0;
/* Storing all the images into a variable */
var images = $('#stage img').removeClass('default').addClass('animationReady');
var dim = { width:images.width(),height:images.height()};
var cnt = images.length;
/* Finding the centers of the animation container: */
var centerX = $('#stage').width()/2;
var centerY = $('#stage').height()/2 - dim.height/2;
function rotate(step,total){
// This function will loop through all the phone images, and rotate them
// with "step" degrees (10 in this implementation) till total > 0
/* Increment the degrees: */
deg+=step;
var eSin,eCos,newWidth,newHeight,q;
/* Loop through all the images: */
for(var i=0;i<cnt;i++){
/* Calculate the sine and cosine for the i-th image */
q = ((360/cnt)*i+deg)*Math.PI/180;
eSin = Math.sin(q);
eCos = Math.cos(q);
/*
/ With the sine value, we can calculate the vertical movement,
/ and the cosine will give us the horizontal movement.
*/
q = (0.6+eSin*0.4);
newWidth = q*dim.width;
newHeight = q*dim.height;
/*
/ We are using the calculated sine value (which is in the range
/ of -1 to 1) to calculate the opacity and z-index. The
/ frontmost image has a sine value of 1, while the backmost
/ one has a sine value of -1.
*/
// eq() extracts the image at the i-th position:
images.eq(i).css({
top : centerY+15*eSin,
left : centerX+200*eCos,
opacity : 0.8+eSin*0.2,
marginLeft : -newWidth/2,
zIndex : Math.round(80+eSin*20)
}).width(newWidth).height(newHeight);
}
total-=Math.abs(step);
if(total<=0) return false;
// Setting the function to be run again in 40 milliseconds (equals to 25 frames per second):
setTimeout(function(){rotate(step,total)},40);
}
// Running the animation once at load time (and moving the iPhone into view):
rotate(10,360/cnt);
$('#phoneCarousel .previous').click(function(){
// 360/cnt lets us distribute the phones evenly in a circle
rotate(-10,360/cnt);
});
$('#phoneCarousel .next').click(function(){
rotate(10,360/cnt);
});
});
To start an animation you just need to call the rotate function with two arguments – a step, and a total rotation, both of which are numbers. Step can be negative, which would mean that the rotation is run in the opposite way. Each time the function is run, total is decremented with the absolute value of the step, and once it reaches zero the animation is stopped.
In a number of places in this code, you can see that I’ve used a specific calculation – 360/cnt. This is done to distribute the phones evenly (360 being the number of degrees in a circle). This way you can add or remove images and they will be properly animated.
Slick MobileApp Website
With this our Slick MobleApp Website is complete!
Wrapping it up
Today we made a complete jQuery & CSS website for a fictional mobile application. You are free to modify the code and use it any way you see fit. If you liked the tutorial be sure to subscribe to our RSS feed, follow us on twitter, or leave a comment in the section below.
This time, we are making a Simple AJAX Commenting System. It will feature a gravatar integration and demonstrate how to achieve effective communication between jQuery and PHP/MySQL with the help of JSON.
Step 1 – XHTML
First, lets take a look at the markup of the comments. This code is generated by PHP in the Comment class, which we are going to take a look at in a moment.
demo.php
<div class="comment">
<div class="avatar">
<a href="http://tutorialzine.com/">
<img src="http://www.gravatar.com/avatar/112fdf7a8fe3609e7af2cd3873b5c6bd?size=50&default=http%3A%2F%2Fdemo.tutorialzine.com%2F2010%2F06%2Fsimple-ajax-commenting-system%2Fimg%2Fdefault_avatar.gif">
</a>
</div>
<div class="name"><a href="http://tutorialzine.com/">Person's Name</a></div>
<div title="Added at 06:40 on 30 Jun 2010" class="date">30 Jun 2010</div>
<p>Comment Body</p>
</div>
The avatar div contains a hyperlink (if the user entered a valid URL when submitting the comment) and an avatar image, which is fetched from gravatar.com. We will return to this in the PHP step of the tut. Lastly we have the name and time divs, and the comment body.
The other important element in the XHTML part is the comment form. It is sent via POST. All fields except for the URL field are required.
demo.php
<div id="addCommentContainer">
<p>Add a Comment</p>
<form id="addCommentForm" method="post" action="">
<div>
<label for="name">Your Name</label>
<input type="text" name="name" id="name" />
<label for="email">Your Email</label>
<input type="text" name="email" id="email" />
<label for="url">Website (not required)</label>
<input type="text" name="url" id="url" />
<label for="body">Comment Body</label>
<textarea name="body" id="body" cols="20" rows="5"></textarea>
<input type="submit" id="submit" value="Submit" />
</div>
</form>
</div>
The form is submitted via AJAX. The validation is performed entirely in the backend by submit.php, as you will see in the jQuery step of the tutorial. Every field has a corresponding label element, with an appropriate for attribute.
Simple AJAX Commenting System
Step 2 – PHP
PHP handles the communication with the MySQL database and generates the markup of the comments. It is also on the receiving end of the AJAX requests and inserts the comment data to the comments table. You can see the code that prints the comments to the page below.
demo.php
/*
/ Select all the comments and populate the $comments array with objects
*/
$comments = array();
$result = mysql_query("SELECT * FROM comments ORDER BY id ASC");
while($row = mysql_fetch_assoc($result))
{
$comments[] = new Comment($row);
}
The MySQL query selects all the entries from the database and fills the $comments array with objects of the comment class, which you will see below. This array is outputted later in the execution of the script.
demo.php
/*
/ Output the comments one by one:
*/
foreach($comments as $c){
echo $c->markup();
}
Each comment has a markup() method, which generates valid HTML code ready to be printed to the page. You can see the definition of this method and the class below.
The class takes a row from the database (fetched with mysql_fetch_assoc() ) and stores it in the private variable $data. It is available only to the methods of the class and cannot be accessed from outside.
comment.class.php – Part 1
class Comment
{
private $data = array();
public function __construct($row)
{
/*
/ The constructor
*/
$this->data = $row;
}
public function markup()
{
/*
/ This method outputs the XHTML markup of the comment
*/
// Setting up an alias, so we don't have to write $this->data every time:
$d = &$this->data;
$link_open = '';
$link_close = '';
if($d['url']){
// If the person has entered a URL when adding a comment,
// define opening and closing hyperlink tags
$link_open = '<a href="'.$d['url'].'">';
$link_close = '</a>';
}
// Converting the time to a UNIX timestamp:
$d['dt'] = strtotime($d['dt']);
// Needed for the default gravatar image:
$url = 'http://'.dirname($_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]).
'/img/default_avatar.gif';
return '
<div class="comment">
<div class="avatar">
'.$link_open.'
<img src="http://www.gravatar.com/avatar/'.
md5($d['email']).'?size=50&default='.
urlencode($url).'" />
'.$link_close.'
</div>
<div class="name">'.$link_open.$d['name'].$link_close.'</div>
<div class="date" title="Added at '.
date('H:i \o\n d M Y',$d['dt']).'">'.
date('d M Y',$d['dt']).'</div>
<p>'.$d['body'].'</p>
</div>
';
}
This script uses gravatar to present avatars in the comments. For those of you, who have not used gravatar, this is a really useful service, which lets you associate an avatar with your email address. The avatar image can easily be fetched by passing an md5() encoded hash of your email address to gravatar.com. This is exactly what we do on line 48.
Notice line 39 above it – the script tries to figure out the URL at which it is located, and determines the exact address of the default_avatar.gif image. This gif is passed to gravatar along the md5 hash, so if no avatar was found for this particular email, the fallback image is displayed instead.
comment.class.php – Part 2
public static function validate(&$arr)
{
/*
/ This method is used to validate the data sent via AJAX.
/
/ It return true/false depending on whether the data is valid, and populates
/ the $arr array passed as a paremter (notice the ampersand above) with
/ either the valid input data, or the error messages.
*/
$errors = array();
$data = array();
// Using the filter_input function introduced in PHP 5.2.0
if(!($data['email'] = filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)))
{
$errors['email'] = 'Please enter a valid Email.';
}
if(!($data['url'] = filter_input(INPUT_POST,'url',FILTER_VALIDATE_URL)))
{
// If the URL field was not populated with a valid URL,
// act as if no URL was entered at all:
$url = '';
}
// Using the filter with a custom callback function:
if(!($data['body'] = filter_input(INPUT_POST,'body',FILTER_CALLBACK,
array('options'=>'Comment::validate_text'))))
{
$errors['body'] = 'Please enter a comment body.';
}
if(!($data['name'] = filter_input(INPUT_POST,'name',FILTER_CALLBACK,
array('options'=>'Comment::validate_text'))))
{
$errors['name'] = 'Please enter a name.';
}
if(!empty($errors)){
// If there are errors, copy the $errors array to $arr:
$arr = $errors;
return false;
}
// If the data is valid, sanitize all the data and copy it to $arr:
foreach($data as $k=>$v){
$arr[$k] = mysql_real_escape_string($v);
}
// Ensure that the email is in lower case (for a correct gravatar hash):
$arr['email'] = strtolower(trim($arr['email']));
return true;
}
The validate() method above (also part of the class) is defined as static. This means that it can be evoked directly like Comment::validate(), without the need of creating an object of the class. What this method does, is validate the input data which is submitted via AJAX.
This method uses the new filter functions, which are available as of PHP 5.2.0. These allow us to easily validate and filter any input data that is passed to the script. For example filter_input(INPUT_POST,’url’,FILTER_VALIDATE_URL) means that we are checking whether $_POST['url'] is a valid URL address. If it is, the function returns the value of the variable, otherwise it return false.
This is really useful, as up until now, we had to use custom regular expressions to validate data (and have series of if statements). Also, another advantage is that this data is fetched before any configuration-specific transformations (like magic quotes) are applied.
We also have the option of specifying a custom function which is going to apply some more advanced modifications of the data, as you can see from lines 31 and 37.
comment.class.php – Part 3
private static function validate_text($str)
{
/*
/ This method is used internally as a FILTER_CALLBACK
*/
if(mb_strlen($str,'utf8')<1)
return false;
// Encode all html special characters (<, >, ", & .. etc) and convert
// the new line characters to <br> tags:
$str = nl2br(htmlspecialchars($str));
// Remove the new line characters that are left
$str = str_replace(array(chr(10),chr(13)),'',$str);
return $str;
}
}
The last method is validate_text, which we are passing as a callback function in the two filter_input calls above. It encodes all special HTML characters, effectively blocking XSS attacks. It also replaces the newline characters with <br /> line breaks.
submit.php
/*
/ This array is going to be populated with either
/ the data that was sent to the script, or the
/ error messages:
/*/
$arr = array();
$validates = Comment::validate($arr);
if($validates)
{
/* Everything is OK, insert to database: */
mysql_query(" INSERT INTO comments(name,url,email,body)
VALUES (
'".$arr['name']."',
'".$arr['url']."',
'".$arr['email']."',
'".$arr['body']."'
)");
$arr['dt'] = date('r',time());
$arr['id'] = mysql_insert_id();
/*
/ The data in $arr is escaped for the mysql insert query,
/ but we need the unescaped text, so we apply,
/ stripslashes to all the elements in the array:
/*/
$arr = array_map('stripslashes',$arr);
$insertedComment = new Comment($arr);
/* Outputting the markup of the just-inserted comment: */
echo json_encode(array('status'=>1,'html'=>$insertedComment->markup()));
}
else
{
/* Outputting the error messages */
echo '{"status":0,"errors":'.json_encode($arr).'}';
}
submit.php receives the comment form data via an AJAX request. It validates it and outputs a JSON object with either the XHTML markup of the successfully inserted comment, or a list of error messages. jQuery uses the status property to determine whether to display the error messages or add the comment markup to the page.
You can see two example responses below.
Successful response
{
"status": 1,
"html": "Html Code Of The Comment Comes Here..."
}
The html property contains the code of the comment, similar to markup in step one.
Failure response
{
"status": 0,
"errors": {
"email": "Please enter a valid Email.",
"body": "Please enter a comment body.",
"name": "Please enter a name."
}
}
On failure, jQuery loops through the errors object, and outputs the errors next to the fields that caused them.
Fancy CSS3 & jQuery Submit Form
Step 3 – CSS
Now that we have all the markup properly generated and displayed on the page, we can move on to styling it.
styles.css – Part 1
.comment,
#addCommentContainer{
/* Syling the comments and the comment form container */
padding:12px;
width:400px;
position:relative;
background-color:#fcfcfc;
border:1px solid white;
color:#888;
margin-bottom:25px;
/* CSS3 rounded corners and drop shadows */
-moz-border-radius:10px;
-webkit-border-radius:10px;
border-radius:10px;
-moz-box-shadow:2px 2px 0 #c2c2c2;
-webkit-box-shadow:2px 2px 0 #c2c2c2;
box-shadow:2px 2px 0 #c2c2c2;
}
.comment .avatar{
/*
/ The avatar is positioned absolutely,
/ and offset outside the comment div
/*/
height:50px;
left:-70px;
position:absolute;
width:50px;
background:url('img/default_avatar.gif') no-repeat #fcfcfc;
/* Centering it vertically: */
margin-top:-25px;
top:50%;
-moz-box-shadow:1px 1px 0 #c2c2c2;
-webkit-box-shadow:1px 1px 0 #c2c2c2;
box-shadow:1px 1px 0 #c2c2c2;
}
The .comment divs and the #addCommentContainer are styled at once because they share most of the styling. A number of CSS3 rules are applied, including rounded corners and a box-shadow. Needless to say, these do not work in older browsers, but as they are purely presentational, the script will still work without them.
styles.css – Part 2
.comment .avatar img{
display:block;
}
.comment .name{
font-size:20px;
padding-bottom:10px;
color:#ccc;
}
.comment .date{
font-size:10px;
padding:6px 0;
position:absolute;
right:15px;
top:10px;
color:#bbb;
}
.comment p,
#addCommentContainer p{
font-size:18px;
line-height:1.5;
}
#addCommentContainer input[type=text],
#addCommentContainer textarea{
/* Styling the inputs */
display:block;
border:1px solid #ccc;
margin:5px 0 5px;
padding:3px;
font-size:12px;
color:#555;
font-family:Arial, Helvetica, sans-serif;
}
#addCommentContainer textarea{
width:300px;
}
label{
font-size:10px;
}
label span.error{
color:red;
position:relative;
right:-10px;
}
#submit{
/* The submit button */
background-color:#58B9EB;
border:1px solid #40A2D4;
color:#FFFFFF;
cursor:pointer;
font-family:'Myriad Pro',Arial,Helvetica,sans-serif;
font-size:14px;
font-weight:bold;
padding:4px;
margin-top:5px;
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;
}
#submit:hover{
background-color:#80cdf5;
border-color:#52b1e2;
}
In the second part of the stylesheet, we style the comment and form elements. Notice the input[type=text] selector, which selects elements depending on the type attribute.
Step 4 – jQuery
Now lets continue with jQuery, which is the last step of this tutorial. After including the library to the bottom of the page (best for the perceived performance of the page) we can start coding the script file.
script.js
$(document).ready(function(){
/* The following code is executed once the DOM is loaded */
/* This flag will prevent multiple comment submits: */
var working = false;
/* Listening for the submit event of the form: */
$('#addCommentForm').submit(function(e){
e.preventDefault();
if(working) return false;
working = true;
$('#submit').val('Working..');
$('span.error').remove();
/* Sending the form fileds to submit.php: */
$.post('submit.php',$(this).serialize(),function(msg){
working = false;
$('#submit').val('Submit');
if(msg.status){
/*
/ If the insert was successful, add the comment
/ below the last one on the page with a slideDown effect
/*/
$(msg.html).hide().insertBefore('#addCommentContainer').slideDown();
$('#body').val('');
}
else {
/*
/ If there were errors, loop through the
/ msg.errors object and display them on the page
/*/
$.each(msg.errors,function(k,v){
$('label[for='+k+']').append('<span class="error">'+
v+'</span>');
});
}
},'json');
});
});
Starting from the top, we have the $(document).ready() call, which binds a function to the DOM content loaded event. The working variable acts as a flag, which tells the script if an AJAX request is in progress (thus preventing double posting).
In the callback function for the POST AJAX request, we check the status property to determine whether the comment was successfully inserted. If it was, we add the received markup to the page after the last comment with a slideDown animation.
If there were problems, we display the error messages by appending an error span to the appropriate label element (the for attribute of the label contains the id of the input that caused the error).
With this our Simple AJAX Commenting System is complete!
Conclusion
To be able to run this script on your server, you need to create the comments table in your MySQL database. You can do this by executing the SQL code found in table.sql from the SQL tab of phpMyAdmin. After this you need to enter your MySQL connection details in connect.php.
You are free to modify and use this code any way you see fit.
What do you think? How would you improve this script?
As you have probably heard by now, CSS3 animations are a powerful tool, which enables you to create animations which run without the need of applying additional scripting to the page. What is even better, in the next generation of browsers we will have even more powerful tools, including 3D transformations (already present in Safari).
But what difference does it make for us today? At the moment only three browsers give you the ability to animate CSS properties – Chrome, Safari and Opera, which together take up only a small part of the browser market. Firefox is expected to soon join the club, and with the impending release of IE9, it suddenly makes sense to start leveraging this technique.
So today we are making something practical – a simple CSS3 animated navigation menu, which degrades gracefully in older browsers and is future-proofed to work with the next generation of browsers.
The XHTML
The menu is organized as an unordered list. This is the most suitable structure for a menu, as it provides an easy way to style the menu links and is semantically correct.
demo.html
<ul id="navigationMenu">
<li>
<a class="home" href="#">
<span>Home</span>
</a>
</li>
<li>
<a class="about" href="#">
<span>About</span>
</a>
</li>
<li>
<a class="services" href="#">
<span>Services</span>
</a>
</li>
<li>
<a class="portfolio" href="#">
<span>Portfolio</span>
</a>
</li>
<li>
<a class="contact" href="#">
<span>Contact us</span>
</a>
</li>
</ul>
Inside each li we have a hyperlink with a span inside it. By default these spans are hidden, and are only shown when you hover over the link. Each link has a unique class name, which is used to give it a unique background and style the inner span, as you will see in a moment.
CSS3 Animated Navigation Menu
The CSS
Once we have the basic structure in place, we can now move to creating the fancy CSS3 effects and styling. This works even on browsers, which do not support CSS3 transition animations (all browsers except Chrome, Safari and Opera, at the moment of this writing) albeit with less glitter. The menu is even perfectly usable in browsers as old as IE6.
styles.css – Part 1
*{
/* A universal CSS reset */
margin:0;
padding:0;
}
body{
font-size:14px;
color:#666;
background:#111 no-repeat;
/* CSS3 Radial Gradients */
background-image:-moz-radial-gradient(center -100px 45deg, circle farthest-corner, #444 150px, #111 300px);
background-image:-webkit-gradient(radial, 50% 0, 150, 50% 0, 300, from(#444), to(#111));
font-family:Arial, Helvetica, sans-serif;
}
#navigationMenu li{
list-style:none;
height:39px;
padding:2px;
width:40px;
}
For the styling of the body background, I first supplied a background color, which acts as a fallback, and then added two CSS3 radial gradients (for Firefox and Chrome/Safari respectfully) as background images. If the visitor’s browser does not support gradients, it will just ignore the rules and go with the plain background color.
You can see in the styles, that most of the rules are preceded by the id of the unordered list – #navigationMenu. This is to prevent collisions with the rest of your page styles, if you incorporate the menu into your site.
styles.css – Part 2
#navigationMenu span{
/* Container properties */
width:0;
left:38px;
padding:0;
position:absolute;
overflow:hidden;
/* Text properties */
font-family:'Myriad Pro',Arial, Helvetica, sans-serif;
font-size:18px;
font-weight:bold;
letter-spacing:0.6px;
white-space:nowrap;
line-height:39px;
/* CSS3 Transition: */
-webkit-transition: 0.25s;
/* Future proofing (these do not work yet): */
-moz-transition: 0.25s;
transition: 0.25s;
}
#navigationMenu a{
/* The background sprite: */
background:url('img/navigation.jpg') no-repeat;
height:39px;
width:38px;
display:block;
position:relative;
}
/* General hover styles */
#navigationMenu a:hover span{ width:auto; padding:0 20px;overflow:visible; }
#navigationMenu a:hover{
text-decoration:none;
/* CSS outer glow with the box-shadow property */
-moz-box-shadow:0 0 5px #9ddff5;
-webkit-box-shadow:0 0 5px #9ddff5;
box-shadow:0 0 5px #9ddff5;
}
The CSS3 transition property is a really powerful one. It enables you to animate changes that occur on a element when a pseudo properties take effect. For example here, when the user moves their mouse over a navigation link, the :hover pseudo-property takes effect, showing the span which is otherwise hidden.
Without the definition of a transition property, this change is instantaneous. But with a transition we can animate it. Here we are telling the browser, that the duration of the animation is 250 milliseconds. You can optionally specify a list of specific properties to be animated instead of all of them.
Transitions are currently only supported in webkit based browsers (Safari and Chrome), but the next version of Firefox is also expected to support them, so we future-proof the script by specifying a -moz-transition.
The CSS3 Transition
styles.css – Part 3
/* Green Button */
#navigationMenu .home { background-position:0 0;}
#navigationMenu .home:hover { background-position:0 -39px;}
#navigationMenu .home span{
background-color:#7da315;
color:#3d4f0c;
text-shadow:1px 1px 0 #99bf31;
}
/* Blue Button */
#navigationMenu .about { background-position:-38px 0;}
#navigationMenu .about:hover { background-position:-38px -39px;}
#navigationMenu .about span{
background-color:#1e8bb4;
color:#223a44;
text-shadow:1px 1px 0 #44a8d0;
}
/* Orange Button */
#navigationMenu .services { background-position:-76px 0;}
#navigationMenu .services:hover { background-position:-76px -39px;}
#navigationMenu .services span{
background-color:#c86c1f;
color:#5a3517;
text-shadow:1px 1px 0 #d28344;
}
/* Yellow Button */
#navigationMenu .portfolio { background-position:-114px 0;}
#navigationMenu .portfolio:hover{ background-position:-114px -39px;}
#navigationMenu .portfolio span{
background-color:#d0a525;
color:#604e18;
text-shadow:1px 1px 0 #d8b54b;
}
/* Purple Button */
#navigationMenu .contact { background-position:-152px 0;}
#navigationMenu .contact:hover { background-position:-152px -39px;}
#navigationMenu .contact span{
background-color:#af1e83;
color:#460f35;
text-shadow:1px 1px 0 #d244a6;
}
In the last part of the styling, we specify 5 different designs for the navigation links. All the background images for the links are contained inside a single sprite file. They have a normal and a hover state one under another. When a hover occurs, the background is offset to show the appropriate version of the background image.
A PSD file is included in the downloadable archive, so you can customize the images as much as you like.
With this our minimalistic CSS3 navigation menu is complete!
Conclusion
Sooner or later, we are going to have a quick access to powerful, hardware accelerated graphics, right in the browser. When this day comes, a whole new world will open to web developers, and we will come even closer to building rich internet applications, which behave exactly like native apps.
Till then, we have to make the best with what we have, and slowly start adopting CSS3 techniques into our work.
What do you think? How would you improve this navigation menu?
Apple has long applied a winning strategy in marketing – create well designed products, have a dedicated fan base, and let the hype build up before every product release.
This is also the case with the latest version of their iPhone. But what I found interesting is the term they coined – “Retina display” and the promo image accompanying it.
The image on apple.com that inspired it all
What made me wonder is if it was possible to turn this static image into a fully functional “Retina effect” with only jQuery and CSS. This is exactly what we are doing today. So grab the demo files from the button above and read on.
UPDATE: Seems Apple folks have also liked the idea, and they’ve implemented it on apple.com.
Step 1 – XHMTL
The markup for the effect is quite straightforward. You can see that we only have a number of divs and an image.
demo.html
<div id="main">
<div id="iphone">
<div id="webpage">
<img src="img/webpage.png" width="499" height="283" alt="Web Page" />
<div id="retina"></div>
</div>
</div>
</div>
The #iphone div displays the iphone frame. Inside it is the #webpage div with the screenshot of the webpage. The screenshot is actually displayed at half its original size, as we are using the same image for both the small version (displayed in the iPhone) and big version, which is shown in the rounded tooltip.
Lastly we have the retina div, which is rounded with CSS3 and displays the big version of the webpage screeshot as its background as it is moved around with the mouse.
Putting it together
Step 2 – CSS
Moving to the CSS part of the tutorial. We are going to style the iphone, webpage and retina divs, so we make the awesome effect possible.
styles.css
#iphone{
/* The iphone frame div */
width:750px;
height:400px;
background:url('img/iphone_4G.png') no-repeat center center;
}
#webpage{
/* Contains the webpage screenshot */
width:499px;
height:283px;
position:absolute;
top:50%;
left:50%;
margin:-141px 0 0 -249px;
}
#retina{
/* The Retina effect */
background:url('img/webpage.png') no-repeat center center white;
border:2px solid white;
/* Positioned absolutely, so we can move it around */
position:absolute;
height:180px;
width:180px;
/* Hidden by default */
display:none;
/* A blank cursor, notice the default fallback */
cursor:url('img/blank.cur'),default;
/* CSS3 Box Shadow */
-moz-box-shadow:0 0 5px #777, 0 0 10px #aaa inset;
-webkit-box-shadow:0 0 5px #777;
box-shadow:0 0 5px #777, 0 0 10px #aaa inset;
/* CSS3 rounded corners */
-moz-border-radius:90px;
-webkit-border-radius:90px;
border-radius:90px;
}
#retina.chrome{
/* A special chrome version of the cursor */
cursor:url('img/blank_google_chrome.cur'),default;
}
#main{
/* The main div */
margin:40px auto;
position:relative;
width:750px;
}
By specifying an absolute positioning on the webpage div, we can apply the vertical and horizontal centering technique, effectively putting the webpage screenshot in the middle of the iphone frame.
The retina div is also assigned an absolute positioning, so it is possible to move it in the jQuery part of the tutorial by just specifying a top and left offsets. This div also has the webpage screenshot as its background (in its original size). Offsetting the background with the movement of the div creates the illusion that it magnifies the small screenshot below it.
The Retina Effect
The retina div also has applied a border radius with a value of exactly half its width, which turns it into a perfect circle (at least in browsers which support the border-radius CSS3 property – Chrome, Safari, Opera & Firefox).
And finally we hide the mouse pointer by providing a blank cursor file (Google Chrome fails to render completely blank cursors, so we provide a special 1px white cursor for it – at least it is better than nothing). The Opera browser completely ignores custom cursors and there are no workarounds, so users using this browser might not enjoy the full experience.
Step 3 – jQuery
If you can remember, a few months ago we did a tutorial here at Tutorialzine, in which we used jQuery to create a photoshoot effect. This time we are using a similar technique to make the “retina effect”, as seen on Apple’s web site.
And since we have all the styling in place, it is a matter of some JavaScript coding with the help of the jQuery library.
script.js
$(document).ready(function(){
/* This code is executed on the document ready event */
var left = 0,
top = 0,
sizes = { retina: { width:190, height:190 },
webpage:{ width:500, height:283 } },
webpage = $('#webpage'),
offset = { left: webpage.offset().left, top: webpage.offset().top },
retina = $('#retina');
if(navigator.userAgent.indexOf('Chrome')!=-1)
{
/* Applying a special chrome curosor,
as it fails to render completely blank curosrs. */
retina.addClass('chrome');
}
webpage.mousemove(function(e){
left = (e.pageX-offset.left);
top = (e.pageY-offset.top);
if(retina.is(':not(:animated):hidden')){
/* Fixes a bug where the retina div is not shown */
webpage.trigger('mouseenter');
}
if(left<0 || top<0 || left > sizes.webpage.width ||
top > sizes.webpage.height)
{
/* If we are out of the bondaries of the
webpage screenshot, hide the retina div */
if(!retina.is(':animated')){
webpage.trigger('mouseleave');
}
return false;
}
/* Moving the retina div with the mouse
(and scrolling the background) */
retina.css({
left : left - sizes.retina.width/2,
top : top - sizes.retina.height/2,
backgroundPosition : '-'+(1.6*left)+'px -'+(1.35*top)+'px'
});
}).mouseleave(function(){
retina.stop(true,true).fadeOut('fast');
}).mouseenter(function(){
retina.stop(true,true).fadeIn('fast');
});
});
In the mousemove function, the current coordinates of the mouse are passed as e.pageX and e.pageY, but they are absolute with relation to the document. Subtracting the position offset of the website div, we end up with relative coordinates for the mouse, which are later used to position the retina div.
This, combined with the blank cursor we set up in the CSS part of the tutorial, creates the retina effect in pure JavaScript and CSS.
Conclusion
As the Flash / HTML5 debate heated up recently, people started looking for ways to achieve the same level of functionality that flash provides, without depending on an external plugin. This is possible for simple interactions, but we still have to make sure that the solution is cross-browser compatible, which is sometimes rather impossible (think of IE here).
Hopefully, today we made something that would easily rival an equivalent Flash solution.
What do you think? How would you improve this effect?
Looking for a good PHP Calendar Solution?
ApPHP Cal
Advanced Power of PHP (APPHP) has a nice Calendar solution called "ApPHP Cal". This powerful PHP calendar solution is very flexible and can be intergrated with other existing PHP projects. For example you could integrate ApPHP Calendar (ApPHP CAL) with existing schedulers, event processors, booking systems, etc. This PHP calendar is very simple to install and easy to implement and use. ApPHP Cal has a professional and clear navigation and very user-friendly interface. The calendar and events can be viewed in various ways like yearly, or monthly, weekly or daily. ApPHP Calendar is, according to the creators, a very complete solution for all your calendar needs.
Want to download this PHP calendar? Go to: http://www.apphp.com/
Introduction
Facebook is a showcase of great UI design. And as it has become a major part of our lives, it has also raised the bar for web development, pushing developers to meet higher expectations.
This however has a good side – it challenges web developers and designers to better themselves and constantly improve their work.
In this tutorial, we are going to learn from the best, and create a facebook-like sign up form. So go ahead and download the demo files to start learning!
The XHTML
We start off by laying down the XHTML backbone, as seen in index.php in the demo files:
<div id="div-regForm">
<div class="form-title">Sign Up</div>
<div class="form-sub-title">It's free and anyone can join</div>
<form id="regForm" action="submit.php" method="post">
<table>
<tbody>
<tr>
<td><label for="fname">First Name:</label></td>
<td><div class="input-container">
<input name="fname" id="fname" type="text" />
</div></td>
</tr>
<tr>
<td><label for="lname">Last Name:</label></td>
<td><div class="input-container">
<input name="lname" id="lname" type="text" />
</div></td>
</tr>
<tr>
<td><label for="email">Your Email:</label></td>
<td><div class="input-container">
<input name="email" id="email" type="text" />
</div></td>
</tr>
<tr>
<td><label for="pass">New Password:</label></td>
<td><div class="input-container">
<input name="pass" id="pass" type="password" />
</div></td>
</tr>
<tr>
<td><label for="sex-select">I am:</label></td>
<td>
<div class="input-container">
<select name="sex-select" id="sex-select">
<option value="0">Select Sex:</option>
<option value="1">Female</option>
<option value="2">Male</option>
</select>
</div>
</td>
</tr>
<tr>
<td><label>Birthday:</label></td>
<td>
<div class="input-container">
<select name="month">
<option value="0">Month:</option>
<?=generate_options(1,12,'callback_month')?>
</select>
<select name="day">
<option value="0">Day:</option>
<?=generate_options(1,31)?>
</select>
<select name="year">
<option value="0">Year:</option>
<?=generate_options(date('Y'),1900)?>
</select>
</div>
</td>
</tr>
<tr>
<td> </td>
<td><input type="submit" class="greenButton" value="Sign Up" />
<img id="loading" src="img/ajax-loader.gif" alt="working.." />
</td>
</tr>
</tbody>
</table>
</form>
<div id="error">
</div>
</div>
We begin by creating a div container for the form, to which we assign the id div-regForm. In it we are going to position the various form components.
Later we create the headings of the form, properly styled with CSS, as you’ll see later.
Then we have the form itself. A note here would be to remember that the form does not actually get submitted by its own – its all done via AJAX, which means that it doesn’t really matter what you are going to put in the method and action attributes.
Inside the form, we position a table, which will allow us to easily create a grid layout for the form labels and fields. There’s been a debate on using tables in this manner since div layout came to fashion, but mind you that facebook itself is using the same technique, which automatically wins it for me.
Every input field has a respective label element serving as a, you guessed it – a field label. We are even using the for attribute, which means that by clicking the label, you select the textbox on the right.
Next comes a select box and after this we have 3 very interesting lines, that I’ve highlighted for you. We use a neat little PHP function to generate all the option elements that go into the select boxes comprising the selection of a birth date. We will talk about this in a minute.
Later we have the Sign Up button, and a little gif, which is hidden by default and shown only when AJAX requests are in progress.
The last div element is our error container, also hidden by default.
Our facebook-like registration form
The CSS
In order to convert our plain XHML coding into something eye-catching and facebook-likey, we need some serious styling.
Lets take a look at our CSS, as defined in demo.css.
/* Page styles */
body,h1,h2,h3,p,td,quote,small,form,input,ul,li,ol,label{
margin:0px;
padding:0px;
}
body{
margin-top:20px;
font-family:Arial, Helvetica, sans-serif;
color:#51555C;
height:100%;
font-size:11px;
}
/* Form styles */
input,select{
padding:3px;
color:#333333;
border:1px solid #96A6C5;
margin-top:2px;
width:200px;
font-size:11px;
}
select{
width:auto;
padding:2px;
}
.formline{
padding:3px;
}
label{
font-size:11px;
text-align:right;
}
table{
width:300px;
}
td{
font-size:11px;
}
.input-container{
padding:1px;
}
#div-regForm,.registered{
border:3px solid #eeeeee;
padding:15px;
background:url(img/bg.jpg) repeat-x #cbd4e4;
color:#203360;
margin:30px auto 40px auto;
width:400px;
}
.form-title,
.form-sub-title{
font-size:20px;
font-family:"Lucida Grande",Tahoma,Verdana,Arial,sans-serif;
font-size:20px;
font-weight:bold;
}
.form-sub-title{
font-weight:normal;
padding:6px 0 15px 0;
}
.greenButton{
width:auto;
margin:10px 0 0 2px;
padding:3px 4px 3px 4px;
color:white;
background-color:#589d39;
outline:none;
border:1px solid #006600;
font-weight:bold;
}
.greenButton:active{
background-color:#006600;
padding:4px 3px 2px 5px;
}
#loading{
left:10px;
position:relative;
top:3px;
visibility:hidden;
}
#error{
background-color:#ffebe8;
border:1px solid #dd3c10;
padding:7px 3px;
text-align:center;
margin-top:10px;
visibility:hidden;
}
Lines 1-6 are where we reset some of the XHTML elements, to ensure that they appear the same in all browsers.
We continue with styling the body section and start styling the form elements.
The first elements we style are input and select. Select shares most of its styling with input, but also differs in width and padding, so we put an additional set of styles to cover them.
Its mostly widths and paddings down to line 81, where we style our sign up button. We have styles for both the normal, non-pressed state, and the active, pressed state. What the active state does, is that it moves the text label of the button to the bottom – right with one pixel, while darkening the background, which gives the illusion of the button being pressed.
The last two styles are #loading and #error, which select the respective elements by their ID’s and hide them with visibility:hidden by default. We will only show them with jQuery when it is appropriate.
The PHP code
Remember, when a few minutes ago I mentioned about the generate_options PHP function?
Here it is, taken right from our functions.php file:
function generate_options($from,$to,$callback=false)
{
$reverse=false;
if($from>$to)
{
$tmp=$from;
$from=$to;
$to=$tmp;
$reverse=true;
}
$return_string=array();
for($i=$from;$i<=$to;$i++)
{
$return_string[]='
<option value="'.$i.'">'.($callback?$callback($i):$i).'</option>
';
}
if($reverse)
{
$return_string=array_reverse($return_string);
}
return join('',$return_string);
}
function callback_month($month)
{
return date('M',mktime(0,0,0,$month,1));
}
/* and here is how we use it (taken from our XHTML code above):
generate_options(1,31); // generate days
generate_options(date('Y'),1900); // generate years, in reverse
generate_options(1,12,'callback_month'); // generate months
*/
This is a neat little peace of code. Now lets explain what it does.
The first thing you notice in our generate_options function are the parameters $from and $to for the range of options to be generated (days, months and years in our case), and an optional parameter $callback, which is a callback function (more on that in a moment).
Inside the function, we define $reverse as false. If we use the function in the following manner: generate_options(1,31), we are just generating a series of option elements and outputting them in the same order. If we switch the positions of the parameters the output is in reverse order.
This is exactly what we do in line 5. We check if the range of $from and $to is reversed and setting $reverse to true. But in the same time, we are exchanging their values, so that we use the same for construct as in our normal generation. If we haven’t done this we would have had to write another for to do this job.
Next we fill an array called $return_string with the generated options, reversing it if necessary and outputting it as string by joining the array elements.
Remember the $callback I talked about earlier? Here comes its part. On line 18 we check if a $callback was supplied – as in generate_options(1,12,’callback_month’), and if it was, we execute the function and supplying the current $i counter’s value. In practice, it is the same as if we were doing callback_month($i).
Later we have the callback_month function, which we use as a callback in the month generation. It basically takes an integer parameter (our $i above), and returns the month name as string.
The jQuery source
Ok, now that we have completed the form’s looks, we need to be able to send the data over to the PHP backend where it is processed. For this purpose, we are using jQuery, which you can include in page by putting these lines of code in your page’s head section:
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>
The library is automatically included from Google’s CDN. After that you can use jQuery’s ajax method.
Here is the code located in script.js.
$(document).ready(function(){
$('.greenButton').click(function(){
register();
});
$('#regForm').submit(function(e) {
register();
e.preventDefault();
});
});
function register()
{
hideshow('loading',1);
error(0);
$.ajax({
type: "POST",
url: "submit.php",
data: $('#regForm').serialize(),
dataType: "json",
success: function(msg){
if(parseInt(msg.status)==1)
{
window.location=msg.txt;
}
else if(parseInt(msg.status)==0)
{
error(1,msg.txt);
}
hideshow('loading',0);
}
});
}
function hideshow(el,act)
{
if(act) $('#'+el).css('visibility','visible');
else $('#'+el).css('visibility','hidden');
}
function error(act,txt)
{
hideshow('error',act);
if(txt) $('#error').html(txt);
}
The first lines of code in $(document).ready get executed after the page has loaded and bind our register() function with the button’s onclick event and our form’s onsubmit event utilizing the preventDefault() method in order to stop the form from being submitted.
And here is the simplicity of the $.ajax method (line 23) – in a few lines of code we send to submit.php by POST all of regForm’s fields (regForm is the ID of our registration form). We receive a response in JSON format (for more on that later) which gets processed by the function given in success. In this example (lines 30-37) we process the returned object and decide whether to show an error, or redirect to a registered-user-only page.
Later we have the hideshow() function which hides or shows an element on the page (for example the rotating gif animation on our form) and the error function which manages the displaying of errors on the form.
But how do we decide whether the information in the form is correct, and where to redirect the user if it is?
This is done in submit.php:
// we check if everything is filled in
if(empty($_POST['fname']) || empty($_POST['lname']) || empty($_POST['email']) || empty($_POST['pass']))
{
die('{status:0,txt:"All the fields are required"}');
}
// is the sex selected?
if(!(int)$_POST['sex-select'])
{
die('{status:0,txt:"You have to select your sex"}');
}
// is the birthday selected?
if(!(int)$_POST['day'] || !(int)$_POST['month'] || !(int)$_POST['year'])
{
die('{status:0,txt:"You have to fill in your birthday"}');
}
// is the email valid?
if(!(preg_match("/^[\.A-z0-9_\-\+]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[A-z]{1,4}$/", $_POST['email'])))
die('{status:0,txt:"You haven\'t provided a valid email"}');
echo '{status:1,txt:"registered.html"}';
Here we check some of the most typical error situations. Every message, that we output, has to be formatted as a javascript object with status and txt properties. They are accessible in our AJAX as msg.txt and msg.status. The main idea here is that errors return a status with a value of 0 and a successful registration returns a status 1, with txt being a URL of a resource accessible only for registered users.
*Note that you’ll have to add code for inserting new database records, creating sessions and a few more error checks on your own, depending on your needs.
And with that our form is finished.
Conclusion
Today we created a fancy looking and functional registration page, inspired by no other than facebook itself. We successfully used jQuery and the $.ajax method to create a real time, asynchronous registration form, complete with error checking and browser redirects.
Introduction
You have seen the effect – a website that has in its corner an animation that looks like a page from a book is being opened (this same effect is used on the BloggyBits website). This is called a page peel effect and is an increasingly popular design element. Up till recently, it was quite a challenge to create something like similar. Luckily for us, now our favorite java script library jQuery has a plugin to do just that – the pagePeel plugin.
The page peel effect - a solution to banner blindness
The includes
In order for this to work we will need to include the jQuery library and the pagePeel plugin itself in the HEAD section of our HTML document.
<head>
<script type="text/javascript" src="/jquery.js"></script>
<script type="text/javascript" src="/pp/jQuery.pagePeel.js"></script>
</head>
Here jquery.js is in the same directory as our html file and jQuery.pagePeel.js is in the pp directory. We have to include them in that order.
After that we have to create a DIV, that is going to be converted to our page corner. It is best that the DIV is positioned in at the bottom of the <body> section, before the closing </body> tag.
...........
<div id="pagePeel"></div>
</body>
</html>
The code
Now that we have all the java script files necessary we need to write the code that is going to create our page peel effect. The code should be positioned in the head section, after the included files.
<script type="text/javascript">
$(document).ready(function() {
$('#pagePeel').pagePeel({
bigAd: '/images/bloggy_page_peel_light.gif',
bigSWF: '/pp/page-peel-big.swf',
adLink: 'http://www.stepinto.net',
adLinkTarget: '_blank'
});
}
</script>
$(document).ready is a special jQuery method that allows us to have code executed after the page has loaded.
Inside, on line 2 we have the special method, .pagePeel that was included in the page with the pagePeel library. We are basically converting the DIV with an id of pagePeel into the page corner animation.
The method takes an object as a parameter and you can see from line 3 to line 6 that we have used several properties. bigAd for the background image of the ad, bigSWF is the address of the default swf flash file that comes with the plugin (it creates the page peel effect), adLink for the address that the animation leads to and adLinkTarget is for the target (in this case it opens in a new window).
You maybe wonder where do we specify the size and the position? The default size is 500×500 pixels and we don’t need to specify it additionally (only if we want a different size). For the purpose you can add the following properties: smallWidth and smallHeight (for the small, inactive version) and bigWidth and bigHeight for the full version. There also more interesting properties for which you can refer to at the plugin’s homepage.
You can change the background graphic anyway you like. It is important that it is the same size as the size of the full version of the animation.
Done! You can see a working demo at BloggyBits.com.
Conclusion
That concludes this tutorial. Creating a page flip animation on a web page has never been that easy. This is an original way to present advertisement, or promotions on your site. The best thing about it is that people have not developed the so called banner blindness (ignoring banner or text advertisement on web sites) for this kind of format. At least for now that is.
If you liked this tutorial you can subscribe to our RSS feed or follow us on twitter.