Follow me and receive all the latest free scripts:

By Email:

Most Popular Posts

New Animated Twitter Heart Button

New Animated Twitter Heart Button

Published November 26, 2015 by , category jQuery

TwitterHeart buttonjQueryAnimatedAnimationCSS3CSS3 Animations

Tutorial presenting how to do a Twitter Heart Button animation using jQuery and CSS3 Animations


In this tutorial I will present you a new Like System called Twitter Heart Button Animation. This heart button system is used by Twitter (below each Tweet). When one clicks, the heart animates and becomes red. A counter is incremented by 1. User may unlike the Tweet by clicking again. The heart button becomes grey and the counter is decremented.

To create this script I use jQuery JavaScript library as usually. I will also introduce you the CSS3 Animations which allows animation of most HTML elements without using JavaScript or Flash.

This is a fully working script including database records. For database queries I use PDO. For more information, check my PDO tutorial.

As a start, please have a look at the live demo.

Live demo

In a working environment (like Twitter Website), registered people, with a unique Id, can vote. Then their votes are registered in the database. So in this script, that everyone can test, I create a unique Id for each of you. This Id is simply your Ip Address without the “.” (dots). For example, if your Ip Address is, your Id will be 100100100100.

Your User Id is: 54146139201

Please try voting for the 3 medias below:

Please Like me


Please Like me


Please Like me


CSS3 Animations

CSS animations make it possible to animate transitions from one CSS style configuration to another, without using JavaScript or Flash. An animation lets an element gradually change from one style to another.

For our Heart System we use this .PNG (click on the image to enlarge in another tab)

Heart Animation

Browser Support for Animations

The numbers in the table specify the first browser version that fully supports the property.

Numbers followed by -webkit-, -moz-, or -o- specify the first version that worked with a prefix.

Property Logo Edge Logo Internet Explorer Logo FireFox Logo Chrome Logo Safari Logo Opera
@keyframes 12.0 10.0 16.0
5.0 -moz-
4.0 -webkit-
4.0 -webkit-
15.0 -webkit-
12.0 -o-
animation 12.0 10.0 16.0
5.0 -moz-
4.0 -webkit-
4.0 -webkit-
15.0 -webkit-
12.0 -o-

@keyframes Rule and sub-properties we use

Keyframes - define the stages and styles of the animation.
from and to - which represents 0% (start) and 100% (complete).

Animation Properties - assign the @keyframes to a specific CSS element and define how it is animated.
animation-name - name of the animation, defined in the @keyframes.
animation-duration - duration of the animation (example: 4s or 600ms).
animation-iteration-count - specifies the number of times an animation should run (1 to infinite).
animation-timing-function - defines the speed curve or pace of the animation.

Files architecture









Database structure sample

id is an auto-increment field. media (an integer) is your Media Id. user_id (an integer or a varchar) is the user unique Id. value is the user's rate (a boolean: 0 for "not like" or 1 for "like").

  `media` int(10) NOT NULL,
  `user_id` varchar(50) NOT NULL,
  `value` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `id` (`id`)


This is the index.php page (the complete structure). I include the CSS style lines (which may be placed in a style.css file) which can be placed in a separate file. I have commented a maximum of lines.

To display a heart button next to a media, simply add the code echo heart(1, $userId); where the first number is the Media Id and the second number is the current User Id.

	<title>Twitter Heart Button Animation</title>
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	.heart_icon {
		background: url("design/twitter-heart-button.png");
		background-size: 2900px;
		background-repeat: no-repeat;
		height: 100px;
		width: 100px;
		cursor: pointer;
		position: relative;

	.on { background-position: right; }

	.off { background-position: left; }

	.heart_icon:hover {	background-position: right;	}

	@-webkit-keyframes heart_animation {
		from {background-position: left;}
		to {background-position: right;}

	@keyframes heart_animation {
		from {background-position: left;}
		to {background-position: right;}

	.heartAnimation {
		-webkit-animation-name: heart_animation;
		-webkit-animation-duration: 1s;
		-webkit-animation-iteration-count: 1;
		-webkit-animation-timing-function: steps(28, end);

		animation-name: heart_animation;
		animation-duration: 1s;
		animation-iteration-count: 1;
		animation-timing-function: steps(28, end);

		background-position: right;

	.num_vote {
		margin-left: -20px;
		font-size: 18px;
		color: #333
	#lineBlock div.oneLine { display: inline-block; vertical-align: middle; }

function heart($mediaId, $userId) { // Function called "heart". We pass 2 arguments, the media Id and our user Id
	include ('_connexion.php'); // Connection to database
	$heart = '<div id="lineBlock">'; // See the CSS, we align the 2 div: heart div and value
	$select = $bdd->prepare('SELECT value FROM heart_votes WHERE media = ? AND user_id = ?'); // We check the current heart value (0 or 1) for our user Id
	$select->execute(array($mediaId, $userId)); 
	$getVal = $select->fetch();
	if (isset($getVal['value'])) { // If the value exists (if we have a record in the database table)
		if ($getVal['value'] == true) $heart .= '<div id="heart'.$mediaId.'" class="oneLine heart_icon on" rel="'.$userId.'"></div>';
		if ($getVal['value'] == false) $heart .= '<div id="heart'.$mediaId.'" class="oneLine heart_icon off" rel="'.$userId.'"></div>';
	} else { // Else we create the ligne in the database
		$insert = $bdd->prepare('INSERT INTO heart_votes (media, user_id, value) VALUES(:media, :user_id, :value)');
		$success = $insert->execute(array(
					'media' => $mediaId,
					'user_id' => $userId,
					'value' => false
		$heart .= '<div id="heart'.$mediaId.'" class="oneLine heart_icon off" rel="'.$userId.'"></div>';
	// Next to the heart we display the number of vote
	$numVote = $bdd->prepare('SELECT value FROM heart_votes WHERE media = '.$mediaId.' AND value = 1');
	$heart .= '<div class="oneLine num_vote" id="heart_num_vote'.$mediaId.'">'.$numVote->rowCount().'</div>';
	$heart .= '</div>';
	return $heart;

$userIP = $_SERVER['REMOTE_ADDR']; // Get User IP address
$userId = str_replace('.', '', $userIP); // Remove the . "dot" from the IP address to have a unique integer

echo '<p>';
echo heart(1, $userId); // Media Id, user Id
echo '</p>';
echo '<p>';
echo heart(2, $userId);
echo '</p>';
<script type="text/javascript" src="jquery/jquery-1.10.1.min.js"></script>
<script type="text/javascript" src="js/tuto-heart-system.js"></script>


PHP code

It's the ajax/tuto-heart-system.php file, called by the Ajax function for data treatment.

include('../_connexion.php'); // Connection to database

if($_POST) {
	$mediaId = $_POST['mediaId']; // Get media Id
	$userId = $_POST['userId']; // Get user Id
	$value = $_POST['value']; // Get the value: 0 or 1
	// Update the database with the new value
	$update = $bdd->prepare('UPDATE heart_votes SET value = :value WHERE media = '.$mediaId.' AND user_id = "'.$userId.'"');
	$update->bindValue(":value", $value);

jQuery code

It's the js/tuto-heart-system.js file.

$(".heart_icon").mouseout(function() {

$(".heart_icon").on("click", function(){ // Click event (when user click on the heart button)
	var getMediaId = $(this).attr("id").split("heart");
	var mediaId = getMediaId[1]; // Result is the media Id from div called "heart"
	// Check if Class Attribut contains "off". If yes then the new value (when user click) will be 1. Else the new value will be 0.
	if ( $(this).attr("class").indexOf('off') !== -1 ) var value = 1; else var value = 0;
	// Get the user Id
	var userId=$(this).attr("rel");
	var dataFields = {'mediaId': mediaId, 'userId': userId, 'value': value}; // We pass the 3 arguments
	$.ajax({ // Ajax
		type: "POST",
		url: "ajax/tuto-heart-system.php",
		data: dataFields,
		timeout: 3000,
		success: function(dataBack){
		error: function() {}
	// If the value is 1 we update the Class and run the CSS3 Animation
	if (value == 1) {
		$("#heart" + mediaId).removeClass("off").addClass("on heartAnimation");
		// We increment the counter (+1)
		$("#heart_num_vote" + mediaId).text(parseInt($("#heart_num_vote" + mediaId).text()) + 1);
	if (value == 0) {
		$("#heart" + mediaId).removeClass("on heartAnimation").addClass("off").css("background-position","left");
		$("#heart_num_vote" + mediaId).text(parseInt($("#heart_num_vote" + mediaId).text()) - 1);


As you can see the .PNG image containing all the heart frames is 2900 pixels with and 100 pixels high. Mean that the heart animation is 100 pixels by 100 pixels. But, you may want to use a smaller heart image, like a 50px by 50px image.

To do so it's very simple, in the <style> tag, replace:
background-size: 2900px; by background-size: 1450px;
height: 100px; by height: 50px;
width: 100px; by width: 50px;


I hope this code snippet will help some of you. It's a very simple and efficient script. Don't forget to share this page on your favorite social media.

If you have any problem, please comment below, I will try to help as much as I can.

If you find a way to make it simpler or if you find nice new features, also post below .

About Simon Laroche
Simon Laroche on Google+
Simon Laroche on Twitter
Simon Laroche on Facebook
Simon Laroche on Pinterest
Simon Laroche on LinkedIn
: I am a Coder, Designer, Webmaster and Expert SEO Consulting, I'm also a wise traveller and an avid amateur photographer. I created the website TipoCode and many others such as Landolia: a World of Photos...

If you need help about this script, please leave a comment below. I reply as much as I can depending of my time, you may also get help from others.
I also offer a paid support, if you are in the need to adapt or create a script...

Leave a comment

Comments (10 comments)

Simon Laroche
Simon Laroche Posted on October 15, 2016
@cem: well, I just told you some ways to do it, but of course you have to reprogram the system and change the code !
For me I can work on it to adapt for your database and needs but it's a payable service and the you need to contact me by email (contact page) for that.
cem Posted on October 14, 2016
thank you simon for your answer, but i am new on using php..
I have changed user_id NOT a unique and value field to a INT(6) on the database, but nothing changed, when different ip's visits the site it duplicates all posts on the database.

can u give me example do i need to change something on script too?
Simon Laroche
Simon Laroche Posted on October 14, 2016
@cem: yes, with my system, as I show only 3 medias (photos to rate), I insert one line per vote. But you have many ways to do. If you have thousands of medias (posts in your case) I guest you should modify the system like this :
- insert one line per post of course (you can't avoid), so you will have a few thousands of lines (no problem !), same you have thousands of lines in your forum->post table
- then you change the user_id field to be NOT a unique user, but all the user list (all their ids)
- In the value field, you change it to a INT(6) for example and you increment it or decrement it every time someone like or unlike. In the same way you add or remove their user id in the user_id field.

Like that you keep ONLY 1 line for 1 post.

Of course, you could think about many other ways to do I guess, it's up to you, but database table may contain millions of lines.
I just think that you could also simply REMOVE the user_id field and use cookies. When someone likes, you place a cookie and you add +1 to the value field. When someone unlike, you remove the cookie and remove -1 to the value field.
Probably better again even if in certain condition (the user delete all its cookies from his computer) less accurate, but who cares? And lighter of course.

Hope you can find the best choice for you :)
Good luck
cem Posted on October 14, 2016
hi simon, i am using your code and everything work perfect, but for each post it creates many db rows when liked, ( for example 1 post has 4 likes and i checked database it has 47 . most of them value is 0), my website will have thousands of posts and if thousands of likes, is it going to be heavy and create problem for database? is there other way to store records simpler?
Simon Laroche
Simon Laroche Posted on August 17, 2016
@Ian: well, I am not sure like that, it can be a thousand things...
Check your variables name, display them to be sure they are not empty and names are correct, test your database connection to see if it works etc...etc...
Ian Posted on August 17, 2016
Hi Simon! this is Ian again .. I tried to execute the code but I get these errors "Undefined variable bdd in line 65 in index.php" I tried debugging it but I can't seem to find the cause of the error. I also get this error "Call to a member function prepare() on a non-object" also in line 65 in index.php. I would really appreciate your help! Thank you for replying!
Simon Laroche
Simon Laroche Posted on August 12, 2016
@Ian: all files are above!
You just need to create the files with the names I said and copy / paste the code and it's working.
Ian Posted on August 12, 2016
Hi Simon!

Is it possible that I can get the whole file of this tutorial? I badly need this for our project. I need to have a heart rating system. Thanks a lot
Simon Laroche
Simon Laroche Posted on March 25, 2016
@Thomas: for the tutorial files, just copy paste to create files yourself in your program.
For the _connection.php it's your database connection file, so inside you put your MySQL code, something like:

define('host', 'localhost');
define('user', 'root');
define('pass', '');
define ('bdd', 'dbname'); $db = @mysql_pconnect(host,user,pass); @mysql_select_db(bdd, $db);
Thomas Posted on March 24, 2016
Congratulations for the tutorial.
But I don't know how to create the _connection.php archive . Do you can help me?

And one more thing, do you can offer all the archives of the tutorial for download?