Building a Twitter Clone Using JQuery

Building a Twitter Clone Using JQuery


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 that 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.

You can Checkout the live demo and source code if you want to refer to it or take a peek as we get started.

The main purpose of this article is to show you how to create the front end and using client-side scripting. Let’s get started.

First, create a twitterclone directory with login.html file.

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/js-cookie@2/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>

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/js-cookie@2/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;
}

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

Twitter Clone
Signup page

Similarly, when you open the profile.html file, it will look like this:

Twitter Clone
profile page

Right now nothing will happen if you click on the tweet button on the profile page. Even the 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.

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.!#$%&'*+\/=?^_`{|}~-]+@[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.

Let’s now create a JavaScript file for profile/wall page. Like before, right-click on twitterclone directory and create a new 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 the 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:

Twitter Clone
undefined profile

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.

Our project is complete and fully functional now.


Share on social media

//