Creating Twitter Clone Using HTML, CSS, JQuery Only

In this project we are going to create a Twitter clone using HTML, CSS and JQuery only. We will create the Registration screen and Twitter wall, where you can post tweets which will have 250 characters limit. You can also retweet and like the tweets. But since we are not using any server and database, so nothing will persist. As soon as you will reload the page, everything will be lost. The main purpose of this article is to show you how to create front end and using client side scripting. We will use Webclipse to create our project. Let’s get started.

Live demo

Creating a Javascript (JQuery) Project in Webclipse

Start Webclipse on your computer system. Choose File  > New > Javascript Project.

Enter a name for your project. We are using “twitterclone” as shown in the image.

Click Finish and your project will be created.

Creating HTML and CSS Files for Twitter Clone

We are going to create the registration screen and wall for Twitter Clone. If you check on Twitter website, there are mainly 3 fields in their registration form – Full Name, Email and Password. In our Registration file we will make 4 fields – Full Name, Email, Password and User Id. User Ids or Usernames are the essential part of Twitter system just like hashtags. In our application, we are not providing any distinct support for hashtags or urls.

Since there is no server side scripting and database involved in this project, there is no meaning of login screen. So we are using the term registration and login interchangeably.

Let’s first create the login.html file. Right click on the “twitterclone” directory in the project explorer window and select New > File (As shown in the below image).

In the next window you will be prompted to enter the file name. Here we are providing “login.html” as our file name. Click Finish after entering the file name.

In the “login.html” file, we are going to insert the HTML code for the registration form. Here is the complete code for the registration screen.

 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8">
         link href="https://necolas.github.io/normalize.css/7.0.0/normalize.css" rel="stylesheet">
         <link href="index.css" rel="stylesheet">
         <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
         <script src="https://cdn.jsdelivr.net/npm/[email protected]/src/js.cookie.min.js"></script>
         <script
             src="https://code.jquery.com/jquery-3.2.1.min.js"
             integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
             crossorigin="anonymous"></script>
         <script src="login.js"></script>
     </head>
     <body class="segoe">
         <div class="topcontainer">
             <div class="topbar">
                 <i class="fa fa-twitter logocenter" aria-hidden="true"></i>
             </div>

             <div class="AppContent wrapper wrapper-signup" id="page-container">
                 <link rel="stylesheet" href="https://abs.twimg.com/a/1511833274/css/t1/t1_signup.bundle.css">
                 <div class="page-canvas">
                     <div class="signup-wrapper">
                         <h1>
                             Join Twitter Clone today.
                         </h1>
                         <div class="t1-form signup " id="phx-signup-form">
                             <div class="textbox">
                                 <div class="prompt name">
                                     <div data-fieldname="name" class="field">
                                         <div class="sidetip">
                                             <p id="nameerror" role="alert" class="blank invalid error">What's your name?</p>
                                         </div>
                                         <input type="text" placeholder="Full name" aria-required="true" maxlength="50" id="full-name" class="">
                                     </div>
                                 </div>

                                 <div class="prompt name">
                                     <div data-fieldname="name" class="field">
                                         <div class="sidetip">
                                             <p id="uiderror" role="alert" class="blank invalid error">Write a user id</p>
                                         </div>
                                         <input type="text" placeholder="User Id" aria-required="true" maxlength="20" id="uid" class="">
                                     </div>
                                 </div>

                                 <div class="prompt email">
                                     <div data-fieldname="email" class="field">
                                         <div class="sidetip">
                                             <p id="emailerror" role="alert" class="invalid error">Please enter a valid email.</p>
                                         </div>
                                         <input type="text" placeholder="Email" aria-required="true" class="email-input" id="email">
                                     </div>
                                 </div>
                                 <div class="prompt password">
                                     <div data-fieldname="password" class="field">
                                         <div class="sidetip">
                                             <p id="passerror" role="alert" class="blank error">Please enter a password.</p>
                                         </div>
                                         <input type="password" placeholder="Password" aria-required="true" id="password">
                                     </div>
                                 </div>
                             </div>
                             <div class="doit">
                                 <div class="sign-up-box">
                                     <input type="submit" value="Sign up" id="submit_button" class="signup EdgeButton EdgeButton--primary EdgeButton--large submit">
                                 </div>
                             </div>
                         </div>
                     </div>
                 </div>
             </div>
         </div>
     </body>
 </html>

The file will look like the below image.

Here in this file, Webclipse is showing some validation issues. You can see three yellow triangles which states that something about this file could be improved. Nothing is wrong with the code but we can improve the performance by loading the stylesheet in the head section of the file.

Similarly, we need to create a stylesheet and one more HTML file. Name these files as “index.css” and “profile.html”.

The code for profile.html is given below –

<!DOCTYPE html>
<html>
     <head>
         <meta charset="utf-8">
         <title>Twitter Wall</title>
         <link href="https://necolas.github.io/normalize.css/7.0.0/normalize.css" rel="stylesheet">
         <link href="index.css" rel="stylesheet">
         <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
         <script src="https://cdn.jsdelivr.net/npm/[email protected]/src/js.cookie.min.js"></script>
 
         <script
             src="https://code.jquery.com/jquery-3.2.1.min.js"
             integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
             crossorigin="anonymous"></script>
         <script src="profile.js"></script> 
     </head>
     <body class="segoe" style="background: rgb(230, 236, 240);">
         <div>
             <div class="wallcontainer">
                 <div class="profilecard">
                     <div class="profilecardhead"></div>
                     <div class="profilecardimagediv">
                         <img src="http://3.bp.blogspot.com/-JCYefwq__2U/TxCfC3s1ZpI/AAAAAAAAKcM/u5mw7qPAL0w/s300-c/Camilla-Belle-6.jpg">
                     </div>

                     <div class="profilecardnameidcont">
                         <div class="profilecardnamecont">
                             <span id="profilecardname"></span>
                         </div>
                         <span id="profilecarduid"></span>
                     </div>

                     <div class="profilecardstatsdiv">
                         <ul class="profilecardstatslist">
                             <li class="profilecardstatslistitem">
                                 <span class="dispblk">
                                     <span class="statslistitemhead">Tweets</span>
                                     <span id="statslistitemcount" class="statslistitemcount">0</span>
                                 </span>
                             </li>

                             <li class="profilecardstatslistitem">
                                 <span class="dispblk">
                                     <span class="statslistitemhead">Following</span>
                                     <span class="statslistitemcount">35</span>
                                 </span>
                             </li>

                             <li class="profilecardstatslistitem">
                                 <span class="dispblk">
                                     <span class="statslistitemhead">Followers</span>
                                     <span class="statslistitemcount">34</span>
                                 </span>
                             </li>
                         </ul>
                     </div>
                 </div>


                 <div class="rightcontainer">
                     <div class="posttweetcontainer">
                         <img class="posttweetprofimg" src="http://3.bp.blogspot.com/-JCYefwq__2U/TxCfC3s1ZpI/AAAAAAAAKcM/u5mw7qPAL0w/s300-c/Camilla-Belle-6.jpg">
                         <div class="ml56px">
                             <div class="posttweettacontainer">
                                 <textarea id="posttweetta" class="posttweetta" placeholder="What's happening?"></textarea>
                                 <div class="posttweetcountcont">
                                     <span class="posttweetcount"><span id="totalchars">0</span>/250</span>
                                 </div>
                             </div>
                             <div class="posttweetbutcont">
                                 <button id="posttweetbut" class="posttweetbut">Tweet</button>
                             </div>
                         </div>
                     </div>
                     <div>
                         <ul id="tweetscontainer" class="tweetscontainer">
 
                         </ul>
                     </div>
                 </div>
             </div>
         </div>
     </body>
</html>

And the code for index.css is –

body {
 color: #14171a;
 font-size: 14px;
 line-height: 20px;
}

body {
 font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
}


.topbar {
 background-image: url("https://abs.twimg.com/a/1512226556/img/t1/lohp_streams_header_bg_v4.png");
 background-size: 100% 100%;
 border-bottom: 1px solid rgba(0, 0, 0, 0.25);
 height: 46px;
 position: relative;
 width: 100%;
 text-align:center;
}

body.segoe {
 font-family: "Segoe UI",Arial,sans-serif;
}


.topbar .container {
 max-width: 1190px;
 height: 100%;
 text-align: center;
 width: auto;
 margin: 0 auto;
 position: relative;
}

.logocenter{
 font-size: 23px !important;
 margin-top: 11px;
 color:white;
}

.topcontainer{
 background:#FFF;
 height: 100%;
}

#submit_button {
 background-color: #1da1f2;
 border: 1px solid #1da1f2;
 border-radius: 100px;
 box-shadow: none;
 color: #fff;
 font-size: 18px;
 font-weight: bold;
 height: auto;
 line-height: 24px;
 padding: 12px 20px;
 position: relative;
 text-align: center;
 white-space: nowrap;
 width: 392px;
}

.wallcontainer{
 width: 900px; overflow: hidden; margin:auto;
}

.profilecard{
 width: 290px; float: left; background: #FFF; margin: 10px; position: relative;
}

.profilecardhead{
 background-color:#ff0000;height:95px;
}

.profilecardimagediv{
 margin:-30px 0 0 8px;padding:1px;background:#FFF;display:inline-block;border-radius:50%;
}

.profilecardimagediv img{
 width:72px;height:72px;border-radius:50%;box-sizing:border-box;border:2px solid #FFF;
}

.profilecardnameidcont{
 position:absolute;top:103px;left:90px;width:185px;
}

.profilecardnamecont{
 font-size:18px;font-weight:bold;line-height:25px;margin:-1px 0 -2px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal
}

#profilecardname{
 color:inherit;text-transform:uppercase;
}

#profilecarduid{
 color:#667786;font-size:14px;padding-right:5px;text-transform:uppercase;
}

.profilecardstatsdiv{
 padding:16px;
}

.profilecardstatslist{
 table-layout:fixed;box-sizing:border-box;display:table;margin:0;min-width:100%;padding:0;list-style:outside none;
}

.profilecardstatslistitem{
 width:1%;vertical-align:bottom;display:table-cell;padding:0;box-sizing:border-box;line-height:1;overflow:hidden;transition:all 0.15s ease-in-out 0s;text-align:inherit;
}

.dispblk{
 display:block;
}

.statslistitemhead{
 color:#657786;font-size:12px;font-weight:bold;letter-spacing:0,02em;line-height:16px;overflow:hidden;transition:color 0.15s ease-in-out 0s;display:block;
}

.statslistitemcount{
 display:block;font-size:18px;font-weight:bold;padding-top:3px;transition: color .15s ease-in-out 0s;color:#ff0000;
}

.rightcontainer{
 width:580px;float:right;margin-top:10px;margin-right:10px;
}

.posttweetcontainer{
 background:#ffe5e5;padding:10px 12px;position:relative;
}

.posttweetprofimg{
 width:32px;height:32px;position:absolute;left:28px;top:13px;border-radius:50%;
}

.ml56px{
 margin-left:56px;
}

.ml58px{
 margin-left:58px;
}

.mt10px{
 margin-top: 10px;
}

.posttweettacontainer{
 border-radius: 8px; border: 1px solid rgb(255, 153, 153); border-image: none; line-height: 20px; box-shadow: 0px 0px 0px 1px #ff9999; background-color: rgb(255, 255, 255);
}

.posttweetta{
 border-width: 1px 1px 0px; border-style: solid solid none; border-color: rgb(230, 236, 240) rgb(230, 236, 240) currentColor; margin: 0px; padding: 8px; outline: 0px; border-radius: 8px; border-image: none; width: 100%; height: 50px; font-family: "Segoe UI",Arial,sans-serif; font-size: 14px; box-sizing: border-box; opacity: 0.8; background-color: rgb(255, 255, 255);
}

.posttweetcountcont{
 overflow: hidden; margin-top: 10px;
}

.posttweetcount{
 margin-right: 10px; display: block; float: right;
}

.posttweetbutcont{
 overflow: hidden; margin-top: 10px;
}

.posttweetbut{
 padding: 6px 16px; border-radius: 100px; border: 1px solid rgb(255, 50, 50); border-image: none; text-align: center; color: rgb(255, 255, 255); line-height: 20px; font-size: 14px; font-weight: bold; white-space: nowrap; position: relative; cursor: pointer; float: right; box-shadow: none; background-color: rgb(255, 50, 50);
}

.tweetscontainer{
 background: rgb(255, 255, 255); list-style: none; margin: 0px; padding: 0px;
}

.tweetcontainer{
 margin: 0px; padding: 10px; overflow: hidden; border-bottom-color: rgb(230, 236, 240); border-bottom-width: 1px; border-bottom-style: solid;
}

.tweetprofimg{
 border-radius: 50%; width: 48px; height: 48px; margin-right: 10px; float: left;
}

.tweetprofname{
 color:#14171a;font-size:14px;font-weight:bold;
}

.tweetprofuid{
 color:#657786;font-size:14px;margin-left:5px;
}

.tweetstats{
 margin-right: 30px; cursor: pointer;
}

.tweetstats i{
 color: rgb(101, 119, 134); font-size: 16px;
}

.tweetstatscount{
 color: rgb(101, 119, 134); line-height: 1; font-size: 12px; font-weight: bold; margin-left: 6px;
}

.red i, .red span{
 color:#f00 !important;
}

Now let’s check how our files are looking in the browser. Right click on the “login.html” and select Open With > Web Browser, as shown in the below image.

The login/registration screen will look like the below image –

Similarly, when you open the “profile.html” file, it will look like this –

Right now nothing will happen if you click on the tweet button on profile page. Even letters counter will not function when you enter text in the box. We have used the picture of Camilla Belle (Isn’t she beautiful and cute?) but if you wish you can change the images in the code.

Let’s make our application functional by adding javascript in it. First of all create a javascript file for login screen. We will call it “login.js”. To create a javascript file, right click on “twitterclone” directory in Project Explorer and select New > Javascript Source File.

In the next screen you will need to enter the name of the javascript file i.e. login.js

The new javascript file will look like this –

“login.js” file will hold the values of all the text fields in the login/registration screen. It will check whether the values are of appropriate type and non empty. Also, it will set the cookies with user id and name so that we can later use them in our profile/wall page. Below is the code of login.js file.

$(function(){
     $name = $('#full-name');
     $nameerror = $('#nameerror');
 
     $uid = $('#uid');
     $uiderror = $('#uiderror');
 
     $email = $('#email');
     $emailerror = $('#emailerror');
 
     $pass = $('#password');
     $passerror = $('#passerror');
 
     $('#submit_button').click(function(){
 
     $error = 0;
     $nameerror.hide();
     $uiderror.hide();
     $emailerror.hide();
 
     if(($nameval = $.trim($name.val())).length == 0)
     {
         $error = 1;
         $nameerror.show();
     }
 
     if(($uidval = $.trim($uid.val()).length == 0))
     {
         $error = 1;
         $uiderror.show();
     }
     else
     {
         $uidval = $uidval.replace(/\s+/g, '');
     }
 
     if(($emailval = $.trim($email.val())).length == 0)
     {
         $error = 1;
         $emailerror.show();
     }
     else if(!(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-][email protected][a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test($emailval)))
     {
         $error = 1;
         $emailerror.show();
     }
 
     if(($passval = $.trim($pass.val())).length == 0)
     {
         $error = 1;
         $passerror.show();
     }
 
     if(!$error)
     {
         Cookies.set('name', $nameval);
         Cookies.set('uid', $uidval);
 
         location.href = 'profile.html';
     }
 });
});

If there are no errors then two cookies will be set – name and uid. Also the current page will get redirected to “profile.html”.

Javascript Made Easy With Webclipse

There are so many wonderful features that Webclipse provides for the development of Javascript application. Let’s see each one of them.

Syntax Highlighting

Webclipse provides a rich set of colors with proper contrast for both the dark and light themes of eclipse so that our code syntax be clear and readable. This helps the developers in understanding the part of code in a jiffy.

Content Assist

Webclipse can help you in predicting the right functions to be called in particular situations. This will make your work fast and error free. If you forget the name of some function then content assist will provide you the list of all the functions from which you can easily recall your needed one. Look at below images to see it in action.

In the above image content assist is showing that there are only two variables which contains the word ’email’. We can use either of them according to our requirements.

In this image, content assist is showing that we can use any of the listed functions over the “$emailerror” variable. “$emailerror” holds the reference of jquery selector for showing errors in the email field on the login page.

Instant Validation

How often do we skip a semicolon at the end of a line? or do we sometime put a semicolon at the end of function? Yes, sometimes we all do while sometimes we commit other types of syntax errors. With Webclipse you will get the realtime validation so that you won’t have to bear the ache of unexpected behaviors during code execution. Below image shows the instant validation functionality of Webclipse.

Here we forgot a paranthesis at the end of if statement and webclipse instantly informed us about the error

Webclipse gives a brief description of where and what the error is

There are a number of other benefits of Webclipse when we are dealing with Javascript or JQuery. Some of them are –

  1. Integrated Debugging.
  2. Call and Type Hierarchies.
  3. Source Refactoring.
  4. Source Formatting.
  5. Occurrence Highlighting.

We will look at all these features in our future articles. Let’s now create a javacript file for profile/wall page. Like before, right click on “twitterclone” directory in project explorer window and select New – Javascript Source File. We will call this file – “profile.js”.

There are few functions of “profile.js” file –

  1. To set the name and userid of the user which was provided in the login page and stored in name and uid cookies by login.js file.
  2. To increase the count of tweets on the left profile card as soon as user posted a tweet.
  3. Keeping track of tweets character counts.
  4. Posting a tweet at the press of tweet button.
  5. Retweet functionality.
  6. Like functionality.

Here is the code of “profile.js” file –

$(function(){
 
     $('#profilecardname').text(Cookies.get('name'));
     $('#profilecarduid').text('@'+Cookies.get('uid'));
 
     $statslistitemcount = $('#statslistitemcount');
 
     $totalchars = $('#totalchars');
 
     $posttweetta = $('#posttweetta');
 
     $tweetscontainer = $('#tweetscontainer');
 
     $posttweetta.keypress(function(e){
     if (e.keyCode == 13 && !e.shiftKey)
     {
         e.preventDefault();
         return false;
     }
 
 });
 
 $posttweetta.keyup(function(e){
     $totchars = $(this).val().length;
         if($totchars <= 250)
         $totalchars.text($totchars);
     else
     {
         $totalchars.text('250');
         $(this).val($(this).val().substring(0, 250));
     }
 });
 
 $('#posttweetbut').click(function(){
     if(($taval = $.trim($posttweetta.val())).length > 0)
     {
         $tweetscontainer.prepend(tweetitem($taval));
         $posttweetta.val(''); 
         $statslistitemcount.text(parseInt($statslistitemcount.text()) + 1); 
     }
 });
 
 $tweetscontainer.on('click', 'span.retweet', function(){
     $tweetstatscount = $(this).children('.tweetstatscount');
     $tweetstatscount.text(parseInt($tweetstatscount.text()) + 1);
 
     $tweetscontainer.prepend(tweetitem($tweetstatscount.closest('.tweetcontainer').find('p').text()));
     $statslistitemcount.text(parseInt($statslistitemcount.text()) + 1); 
 });
 
 $tweetscontainer.on('click', 'span.like', function(){
     $tweetstatscount = $(this).children('.tweetstatscount');
     if($(this).hasClass('red'))
     {
         $(this).removeClass('red');
         $tweetstatscount.text(parseInt($tweetstatscount.text()) - 1);
     }
     else
     {
         $(this).addClass('red');
         $tweetstatscount.text(parseInt($tweetstatscount.text()) + 1);
     }
 });
 
 function tweetitem($taval)
 {
     return '<li class="tweetcontainer">'+
                 '<img class="tweetprofimg" src="http://3.bp.blogspot.com/-JCYefwq__2U/TxCfC3s1ZpI/AAAAAAAAKcM/u5mw7qPAL0w/s300-c/Camilla-Belle-6.jpg">'+
                 '<span class="tweetprofname">'+Cookies.get('name')+'</span>'+
                 '<span class="tweetprofuid">@'+Cookies.get('uid')+'</span>'+
                 '<div class="ml58px">'+
                     '<p style="margin: 0px;">'+$taval+'</p>'+
                     '<div class="mt10px">'+
                         '<span class="retweet tweetstats">'+
                             '<i class="fa fa-retweet"></i>'+
                             '<span class="tweetstatscount">0</span>'+
                         '</span>'+
                         '<span class="like tweetstats">'+
                             '<i class="fa fa-heart-o"></i>'+
                             '<span class="tweetstatscount">0</span>'+
                         '</span>'+
                     '</div>'+
                 '</div>'+
             '</li>';
 }
});

One more thing we can add in this project. Suppose somebody directly opened the “profile.js” file without logging in then what will happen? There will be no set cookies for their name and user id. This will provide null and undefined values. Look at the below image –

Here we are not getting any name on the card and userid field is undefined.

To solve this problem, we can explicitely check whether the cookies for name and userid are set or not. If they are not set then we can redirect the profile page to the login page. The code for this task is written below –

 <script>
     if(Cookies.get('name') == undefined || Cookies.get('uid') == undefined)
         location.href = 'login.html';
 </script>

We need to insert this code in the head section of “profile.html” file and after we have included the Cookie.js external script. Look at the below image –

Our project is complete and fully functional now.

Note: Since we have not setup any server and is using file:/// protocol, Google Chrome is not setting the cookies but Firefox is working fine. You can either setup a server or try it in Firefox.

 

View On GITHUB

About the author

akash mittal

Akash Mittal is a computer scientist and entrepreneur with core skills in web development. After creating several projects in the field of social networks, education, finance, pc games and virtual reality, he put his hands in IOT to create the most affordable smart switch of worth $1

Pin It on Pinterest

Shares