Home » Blog » How To Build Web-Site With AJAX (HTML+CSS+JS+PHP) . . . XHR & jQuery . . .For Beginners

How To Build Web-Site With AJAX (HTML+CSS+JS+PHP) . . . XHR & jQuery . . .For Beginners

원문보기

Contents

Building Simply Chat

At first, let’s build a simply chat on HTML+CSS and PHP+MySQL.

Designing DB

1. Go to phpMyAdmin.

2. Create a new database, named “chatdb“.

3. Create a new table, named “Posts“, with 4 columns:

  • “id” column of type INT, autoincrement (set A_I checkbox), PRIMARY (index);
  • “nick” column of type VARCHAR and length 100;
  • “post_text” column of type TEXT;
  • “post_dt” column of type DATETIME, by default is CURRENT_TIMESTAMP.

Creating Web-Site.

1. At first, let’s create a main page. It is comments list and posting form in bottom. All it will be in index.php file. Let’s create this file and write something like this:

<html>
<head>
</head>
<body>

<?php

$mysqli = new mysqli("127.0.0.1", "root", "", "chatdb");
/* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */
/* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */
/* "chatdb" is DB's name. */
/* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */

$result = $mysqli->query("SELECT * FROM posts;");

?>

<style>

/* All CSS is very simplified. I provide it for example and no more.
 * In Chrome it works tolerably, but in IE and Firefox it works very poorly.
 */

.content {
  display: table;
  
  width: 50%;
  min-width: 400px;
  height: 80%;
  
  /* Center horizontally and vertically */
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  margin: auto;
  
  /* Design */
  border: 1px solid;
  background-color: silver;
  padding: 5px;
}

/* For mobile devices */
@media (max-width: 400px) {
  .content {
    width: 100%;
    min-width: 0;
    padding: 0px;
  }
}
  
</style>

<div class="content">

<!-- Comments List -->
<div id="comments" style="overflow-y: scroll; height: 100%;">

<?php

if ($result) {
  while ($post = $result->fetch_object()){ 
    $nick = $post->nick;
    $post_dt = $post->post_dt;
    $post_text = $post->post_text;
    
    echo "<b>$nick</b> ($post_dt):<br>";
    echo "$post_text<br>";
    echo "<br>";
  } 
  
  $result->close();
}

?>

</div>

<?php

$mysqli->close();

?>

<!-- Post Comment Form -->
<form action="post.php" method="post" style="height: 0; display: table-row;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input type="submit"></input>
</form>

</div>

<!-- Scrolls List To Down -->
<script type="text/javascript">

var divComments = document.getElementById('comments');
divComments.scrollTop = divComments.scrollHeight;

</script>

</body>
</html>

2. Next, let’s create post.php file and write this:

<?php

$nick = $_POST['nick'];

$post_text = $_POST['text'];

$mysqli = new mysqli("127.0.0.1", "root", "", "chatdb");

$nick = $mysqli->real_escape_string($nick);
$nick = htmlspecialchars($nick);

$post_text = $mysqli->real_escape_string($post_text);
$post_text = htmlspecialchars($post_text);

$mysqli->query("INSERT INTO posts (nick, post_text) VALUES ('$nick', '$post_text');");

$mysqli->close();

/* Redirect To Main Page */
header('Location: ' . $_SERVER['HTTP_REFERER']);

?>

3. Open your chat in any modern Chrome-based browser. All works:

4. Write a nick and a text, and click submit button. All also works:

When we submitting form, it redirects us from main page to post.php page. Post.php momentally inserts the data to table and redirects we back to main page.

Also, post.php contains a basic XSS and SQL injection protection.

For simplification, we doesn’t send client’s time from JS to post_dt on INSERT, and it sets to its default value – CURRENT_TIMESTAMP, which presents the current date and time on the server.

When count of comments is more than screen can accommodate, comments div overflows with vertical scrollbar. On page load, JS automatically scrolls scrollbar to bottom, i.e. to recent comments.

What It Wrong Here?

Problem No. 1. New unread comments from other users do not loading from DB automatically, without manual page reload.
This is very, very serious problem for any chat.
How to fix it?
Obviously, one way is to do a HTTP-request to back-end automatically (i.e. via JS) and asynchronously (read below that is it), i.e. one way is to implement an AJAX in any its form.

Problem No. 2. Also, this is wrong that submit button reloads page (redirects to second page and next redirects back).
Page’s reload resets the “Nick” input field (as well as any other changes which made on client).
How to fix it?
Of course, we can implement a workaround to save these changes and reload on page reload. But is not it effectively remove the cause rather than the consequences? We can just remove page reload and this problem will be solved automatically.

Problem No. 2.1. Do you see anything strange here?

Quote:

[submitting form] redirects us from main page to post.php page. Post.php momentally inserts the data to table and redirects we back to main page

Is it too momentally?

Our case with chat is very easy – we uploading only 2 short text parameter. But what if we will want to improve our chat, via adding the attaches, i.e. images and video? Video can weight a few megabytes, what in this case?

Let’s try. Let’s slightly modify our post.php, after $mysqli->query() let’s add this :

for ($i = 0; $i < 1000000000; $i++) { }

And try to post any comment. What do we see? No, UI not freezing, but browser waiting until the connection is closed:

…and finally:

Yes, if post.php causes any unhandled error, the chat disappears, and users sees the blank window with an incomprehensible error message. To go back to chat, user should press “Back” button in browser. And this is an Problem No 2.2.

Let’s remove the hard loop from post.php and go fix these problems.

Implementing Simply AJAX In Simply Chat

Refreshing Comments Automatically

That is webpage without AJAX? It is webpage what reloads fully.

That is webpage with AJAX? It is webpage what reloads partly!

And where to get the page’s parts? We should divide our page into parts, in the way that server will generate page partly.

Page will consist of two parts – the comments block, and other page content. Comments will load & reload separately from other page.

Let’s make the first part, i.e. comments block.

At first, let’s create an empty file, named getcomments.php.

Next, let’s go to index.php, to cut out the comments block and next separate it.

1. Let’s cut MySQL initialization block out from index.php:

<?php

$mysqli = new mysqli("127.0.0.1", "root", "", "chatdb");
/* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */
/* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */
/* "chatdb" is DB's name. */
/* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */

$result = $mysqli->query("SELECT * FROM posts;");

?>

And paste it to getcomments.php.

2. Next, let’s cut content of comments div’s out from index.php:

<?php

if ($result) {
  while ($post = $result->fetch_object()){ 
    $nick = $post->nick;
    $post_dt = $post->post_dt;
    $post_text = $post->post_text;
    
    echo "<b>$nick</b> ($post_dt):<br>";
    echo "$post_text<br>";
    echo "<br>";
  } 
  
  $result->close();
}

?>

And paste it to getcomments.php after MySQL initialization.

3. Next, let’s cut MySQL close block from index.php:

<?php

$mysqli->close();

?>

And also paste it to end of getcomments.php.

4. Finally, let’s remove our JS-script which scrolls comments list to bottom.

<!-- Scrolls List To Down -->
<script type="text/javascript">

var comments = document.getElementById('comments');
comments.scrollTop = comments.scrollHeight;

</script>

Don’t worry, it’s just temporarily.

5. Done. Now we should have something like this:

index.php:

<html>
<head>
</head>
<body>

<style>

/* All CSS is very simplified. I provide it for example and no more.
 * In Chrome it works tolerably, but in IE and Firefox it works very poorly.
 */

.content {
  display: table;
  
  width: 50%;
  min-width: 400px;
  height: 80%;
  
  /* Center horizontally and vertically */
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  margin: auto;
  
  /* Design */
  border: 1px solid;
  background-color: silver;
  padding: 5px;
}

/* For mobile devices */
@media (max-width: 400px) {
  .content {
    width: 100%;
    min-width: 0;
    padding: 0px;
  }
}
  
</style>

<div class="content">

<!-- Comments List -->
<div id="comments" style="overflow-y: scroll; height: 100%;">

</div>

<!-- Post Comment Form -->
<form action="post.php" method="post" style="height: 0; display: table-row;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

</div>

</body>
</html>

getcomments.php:

<?php

$mysqli = new mysqli("127.0.0.1", "root", "", "chatdb");
/* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */
/* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */
/* "chatdb" is DB's name. */
/* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */

$result = $mysqli->query("SELECT * FROM posts;");

?>

Probably, now our page is divided.

Let’s verify it. Go to http://<our_host>/getcomments.php (i.e. http://localhost/getcomments.php for local server).

Next, go to main page, http://<our_host>/index.php (i.e. http://localhost/).

That’s fantastic! We really, successfully divided our page into parts, in a few minutes!

But let’s not stop there. Let’s now “glue” these parts via AJAX. How to do that?

1. At first, we should create a empty JS script on main page:

<script type="text/javascript">
alert('Test');
</script>

We should place it after our content div, i.e. at end of the body node’s content, before </body> closing tag.

2. Let’s make a HTTP GET query from JS to getcomments.php.

For this, we can use the XMLHttpRequest (XHR) class:

<script type="text/javascript">

var xhr = new XMLHttpRequest();
xhr.open('GET', '/getcomments.php', false);
xhr.send(null);
if (xhr.status == 200) {
  alert(xhr.responseText);
}

</script>

It works, but it not considers old IE versions which doesn’t support such initialization of XMLHttpRequest.

For more cross-browser way, let’s go to <head> tag content. and put this:

<script type="text/javascript">

function getXmlHttp(){
  var xmlhttp;
  try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (E) {
      xmlhttp = false;
    }
  }
  
  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    xmlhttp = new XMLHttpRequest();
  }
  
  return xmlhttp;
};

</script>

And our main script (in end of <body>) replace with it:

<script type="text/javascript">

var xhr = getXmlHttp();
xhr.open('GET', '/getcomments.php', false);
xhr.send(null);
if (xhr.status == 200) {
  alert(xhr.responseText);
}

</script>

Let’s try it. As we can see, now JS getting the content of getcomments.php page, and shows in alert.

2.1. But a question: is it really AJAX (Asynchronous Javascript And Xml)?

It is really AJAX, because server’s response formatted in HTML (which based on XML).

But is it really AJAX, is it really asynchronously?

Let’s try. Let’s add this already familar line into any place beetween <?php and ?> in getcomments.php:

for ($i = 0; $i < 1000000000; $i++) { }

And what we see now on page load?

At the beginning, the page hangs out, its UI freezes (becomes not responding to left/right mouse clicks):

Next, Chrome shows us the intrusive peesky window which offers we kill this page:

It is not AJAX! It is JAX! How to make it asynchronously?

Fortunately, XHR also supports the asynchronous mode:

<script type="text/javascript">

var xhr = getXmlHttp();
xhr.open('GET', '/getcomments.php', true); /* true for asynchronous */
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    if(xhr.status == 200) {
      alert(xhr.responseText);
    }
  }
};
xhr.send(null);

</script>

In this case, browser does not waits for response in main UI thread, it runs the request in another thread (i.e. asynchronously), and on done calls the onreadystatechange event in main UI thread context.

Now, all right – page fully available until request runs, and after response got, it shows the alert.

We can remove our:

for ($i = 0; $i < 1000000000; $i++) { }

and continue the work.

3. Let’s put this content to div, instead of alert. Replace it:

alert(xhr.responseText);

with it:

var divComments = document.getElementById('comments');
divComments.innerHTML = xhr.responseText;

All comes back, client again seees “glued” page with comments block

4. Next, let’s set an interval to automatically check for new comments from time to time… and also reestablish our removed auto-scroll script. Result:

<html>
<head>

<script type="text/javascript">

function getXmlHttp(){
  var xmlhttp;
  try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (E) {
      xmlhttp = false;
    }
  }
  
  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    xmlhttp = new XMLHttpRequest();
  }
  
  return xmlhttp;
};

</script>

</head>
<body>

<style>

/* All CSS is very simplified. I provide it for example and no more.
 * In Chrome it works tolerably, but in IE and Firefox it works very poorly.
 */

.content {
  display: table;
  
  width: 50%;
  min-width: 400px;
  height: 80%;
  
  /* Center horizontally and vertically */
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  margin: auto;
  
  /* Design */
  border: 1px solid;
  background-color: silver;
  padding: 5px;
}

/* For mobile devices */
@media (max-width: 400px) {
  .content {
    width: 100%;
    min-width: 0;
    padding: 0px;
  }
}
  
</style>

<div class="content">

<!-- Comments List -->
<div id="comments" style="overflow-y: scroll; height: 100%;">

</div>

<!-- Post Comment Form -->
<form action="post.php" method="post" style="height: 0; display: table-row;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

</div>

<script type="text/javascript">

var divComments = document.getElementById('comments');
  
function loadComments() {
  var xhr = getXmlHttp();
  xhr.open('GET', '/getcomments.php', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        if (xhr.responseText !== divComments.innerHTML) {
          divComments.innerHTML = xhr.responseText;
          
          divComments.scrollTop = divComments.scrollHeight;
        }
      }
    }
  };
  xhr.send(null);
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

</body>
</html>

Now Problem No. 1 is fixed. New comments from other chat users (other browsers tabs, windows or instances) are getting automatically every 1000 msec (1 sec).

But posting our comments already causes our page reload.

Post Comment Without Reload

As you can see above, XHR helps we send HTTP GET-request without page reload & GUI freezing (asynchonously).

Now we also should send HTTP-query asynchronously, but for this once it t is POST query to post.php, not GET.

And, naturally, XHR also allows it. We will use its send() method. For GET, we passed null to it. For POST, we will pass a request body.

Also, we should add the “Content-Type: application/x-www-form-urlencoded” header, to let server know what format used in sending data.

Note: if we don’t know what to send, we can capture regular form’s request by Fiddler or any other HTTP-sniffer, and just simulate this query. HTTP-sniffer is irreplaceable tool for working with HTTP/HTTPS. It helps we see all headers and bodies of all HTTP(S)-requests which sending on the system. I prefering the Fiddler, it is free, modern and very simply tool, which supports both HTTP/HTTPS and both Win x86/x64.

1. At first, let’s create new empty JS script in HTML. Place it before <form> tag, because this script will be used by post comment form (form will call this script on submit).

2. In this script, let’s implement the function which will send our nick & comment’s text to post.php.

<script type="text/javascript">

function postComment(nick, text) {
  var xhr = getXmlHttp();
  xhr.open('POST', '/post.php', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */
      }
    }
  };
  xhr.send('nick=' + nick + '&text=' + text); /* joining the data in format simulates form */
};

</script>

3. Next, let’s hook the page reload on submit:

<form ... onsubmit="return false;">

And add the postComment’s call:

<form action="post.php" method="post" style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;">

4. Finally, we optionally can remove “action” and “method” attributes of form:

<form style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;">

5. Result:

<html>
<head>

<script type="text/javascript">

function getXmlHttp(){
  var xmlhttp;
  try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (E) {
      xmlhttp = false;
    }
  }
  
  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    xmlhttp = new XMLHttpRequest();
  }
  
  return xmlhttp;
};

</script>

</head>
<body>

<style>

/* All CSS is very simplified. I provide it for example and no more.
 * In Chrome it works tolerably, but in IE and Firefox it works very poorly.
 */

.content {
  display: table;
  
  width: 50%;
  min-width: 400px;
  height: 80%;
  
  /* Center horizontally and vertically */
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  margin: auto;
  
  /* Design */
  border: 1px solid;
  background-color: silver;
  padding: 5px;
}

/* For mobile devices */
@media (max-width: 400px) {
  .content {
    width: 100%;
    min-width: 0;
    padding: 0px;
  }
}
  
</style>

<div class="content">

<!-- Comments List -->
<div id="comments" style="overflow-y: scroll; height: 100%;">

</div>

<script type="text/javascript">

function postComment(nick, text) {
  var xhr = getXmlHttp();
  xhr.open('POST', '/post.php', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */
      }
    }
  };
  xhr.send('nick=' + nick + '&text=' + text);
};

</script>

<!-- Post Comment Form -->
<form style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

</div>

<script type="text/javascript">

var divComments = document.getElementById('comments');
  
function loadComments() {
  var xhr = getXmlHttp();
  xhr.open('GET', '/getcomments.php', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        if (xhr.responseText !== divComments.innerHTML) {
          divComments.innerHTML = xhr.responseText;
          
          divComments.scrollTop = divComments.scrollHeight;
        }
      }
    }
  };
  xhr.send(null);
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

</body>
</html>

Now our simply AJAX implementation come at end.

But this is not the end but only the beginning. There is still much to be learned and modified.

Simplifying Our JS Via jQuery

What Is jQuery. Why It Is Interesting

I think you remember our self-made getXmlHttp() function. It is high-level, cross-browser wrapper for constructor of XMLHttpRequest class. Wrapper is programming interface which abstracts us from some raw library (i.e. standard JS library) and its imperfections.

And initializing of XHR object is not unit case where abstraction is useful.

For example, let’s look on this construction which familar for any JS coder:

var divComments = document.getElementById('comments');

I hope you see that is isn’t convenient to type so many letters as in ‘document.getElementById’, especially ifwe using an IDE without code suggestions/autocompletition, or just no IDE.

More convenient, flavour and short way is to do otherwise:

1.  Only once declare own hi-level wrapper for ‘document.getElementById’ in <head>, i.e.

<script type="text/javascript">
function getById(id) {
  return document.getElementById(id);
}
</script>

2. Next, we can just use this function in any place where it is required:

var divComments = getById('comments');

Yes, it looks a bit unusual. But just try it to use – and next, long raw way will just turn your stomach.

Or look other, more graphic sample, and against from our chat’s code:

In Raw JS:

var xhr = getXmlHttp();
xhr.open('GET', '/getcomments.php', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      if (xhr.responseText !== divComments.innerHTML) {
        divComments.innerHTML = xhr.responseText;
        
        divComments.scrollTop = divComments.scrollHeight;
      }
    }
  }
};
xhr.send(null);

Own Hi-Level Wrapper:

function httpGet(url, successCallback) {
  var xhr = getXmlHttp();
  xhr.open('GET', url, true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        successCallback(xhr.responseText);
      }
    }
  };
  xhr.send(null);
};

Usage:

httpGet('/getcomments.php', function (resp) {
  alert(resp);
});

…What conclusions can we draw from all this?

Obviously, the hi-level wrappers are really nice things, if they properly and intelligently designed.

What should we do? Should we quit everything and go build our own hi-level wrapper library (.js file) for all occasions?

Yes, each of us can start leisurely pick his own library of functions really used in regular work projects, and polishing them to extremely convenience. In the end, it will turn out well.

But it will take a lot of time and eventually will take a lot of experience. There is much easier to just use existing popular 3rd-party hi-level wrappers (and they really exist, in a considerable count). Most approved & useful JS library is jQuery.

jQuery vs Raw JS. Some Popular Myths

Myth No. 1: If JS, Then Not jQuery. If jQuery, Then Not JS.

This fundamental delusion occupes not only front-end development, not only web-programming, and not only programming. It occupes all us life.

Unintelligent, backward, imprinting people always obsessed with questing for best programming language, best library, best IDE, best way, and other best tool for all occasions. They are not willing to learn new things and always trying to confine by some narrow range of tools, and really doesn’t understand while it is impossible.

This is just a manifestation of human ignorance, which should be eradicated through constant self-improvement.

The first golden rule of self-improving human is: do not make loud premature conclusions before you do not tried subject by youself.

The second golden rule of self-improving human is: if the subject really useless for your current tasks, it does not means that it is useless at all.

Different tools are designed for different cases. If desired, we can combine any languages in one product if we need. There are no insurmountable technical barriers.

Moreover, there are no problems to combine raw tools with the wrappers.

There are no problems to combine raw JS and jQuery.

You can gradually and partially refactory and port your code to jQuery, and use the raws there jQuery doesn’t contains wrappers at all, or doesn’t contain wrappers which needed for you.

For example, we have such code in raw JS:

var divMain = document.getElementById('main');

var aLink = document.createElement('a');
aLink.href = 'http://codeproject.com/';
aLink.innerHTML = 'Link';
aLink.style.opacity = 0.8;

divMain.appendChild(aLink);

Let’s include jQuery to page (see bellow how to do it), and port this code to jQuery, in 4 stages.

Stage 1:

var divMain = $('#main')[0]; /* [0] gets native HTMLElement */

var aLink = document.createElement('a');
aLink.href = 'http://codeproject.com/';
aLink.innerHTML = 'Link';
aLink.style.opacity = 0.8;

divMain.appendChild(aLink);

Stage 2:

var divMain = $('#main'); /* Now no [0] is needed, because ...*/

var aLink = document.createElement('a');
aLink.href = 'http://codeproject.com/';
aLink.innerHTML = 'Link';
aLink.style.opacity = 0.8;

divMain.append(aLink); /* ... because native method changed to jQuery method */

Stage 3:

var divMain = $('#main');

var aLink = $("<a />", {
  href: "http://codeproject.com/",
  text: "Link"
});
aLink[0].style.opacity = 0.8; /* Against [0] as "gate" to native */

divMain.append(aLink);

Stage 4:

var divMain = $('#main');

var aLink = $("<a />", {
  href: "http://codeproject.com/",
  text: "Link",
});
aLink.css('opacity', '0.8');

divMain.append(aLink);

Stage 4.1 (alternative):

var divMain = $('#main');

var aLink = $("<a />", {
  href: "http://codeproject.com/",
  text: "Link",
  style: "opacity: 0.8;"
});

divMain.append(aLink);

Stage 4.1.1 (short alternative, with jQuery it is much more readable than in raw JS):

$('#main').append($("<a />", {
  href: "http://codeproject.com/",
  text: "Link",
  style: "opacity: 0.8;"
}));

Stage 4.1.1.1 (short, more shapely alternative):

$("<a />", {
  href: "http://codeproject.com/",
  text: "Link",
  style: "opacity: 0.8;"
}).appendTo('#main');

You may stop your work and save changes after any of these stages, and ignore any stage, and your website will continue to run. Just look in JS console and test on localhost before commit.

Myth No. 2: jQuery Slows Your Website

Really? How on earth?

It jQuery loads slowly?

No. Latest, minified jQuery’s version (from Google Developers) weighs only 84 380 bytes. Its non-minified version weighs 247 597 bytes. (jquery-1.11.3.min.js weighs 95 957 bytes, and jquery-1.11.3.js weighs 284 394 bytes.)

Is it a lot or a little? Let’s go to https://www.google.com/ or any other large website and download its only HTML content (without any images, scripts, etc.). How much does it weigh? It weighs 325 676 bytes. And it loading momentally. Nobody complains.

Also we should note that jQuery reduces the size of scripts based on it, which reduces total website’s weight.

Is jQuery evaluates slowly?

No. Let’s try download and eval() the latest jQuery:

var start = new Date().getTime();

var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', false);
xmlhttp.send();
eval(xmlhttp.responseText);

var end = new Date().getTime();
var time = end - start;
alert('Time, msec: ' + time);

In my case this test holds only 350 msec, no more. For non-minified jQuery 2.1.4 it holds 450 msec, no more.

I have very cheap internet (~$4 per month) with speed of 10-40 MBits, not more.

Is jQuery runs your tasks slowly?

No. I generated the HTML file with 2000 divs, using such Python 2.7 script:

file = open('test.html', 'w+')

for i in range(0, 1000):
  file.write("<div id=main" + str(i) + ">Test</div>")

file.close()

Next, I added the

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

to this file,

Finally, I opened it in browser, and in JS console tried:

var start = new Date().getTime();

for (var i = 0; i < 1000; i++) {
  var el = document.getElementById('main' + i);
  el.innerHTML = 'Passed';
}

var end = new Date().getTime();
var time = end - start;
alert('Time, msec: ' + time);

Result: 123 msec.

Next, with jQuery:

var start = new Date().getTime();

for (var i = 0; i < 1000; i++) {
  var el = $('#main' + i);
  el.html('Passed');
}

var end = new Date().getTime();
var time = end - start;
alert('Time, msec: ' + time);

Result: 239 msec.

239 – 123 = 116 msec. What’s 0.116 sec? You do not feel it, if you do not try.

We can ask: but why only 1000? Why not 10000, not 1000000, not 1000000, not 9007199254740991?

The answer is: where is website which queries selector at least at 10000 times? Nowhere.

Test should contain a real task, not sports hardcore.

Myth No. 3: jQuery Is Just Useless

What can I answer for it?

If personally you have enough time and a wish to avoid jQuery, personally you can avoid it.

If jQuery is useless in some your project (i.e. your own JS library), you can and should avoid it. Really. Avoid to write your JS libraries as plugins of jQuery if it doesn’t needed there.

Simply Using jQuery In Our Simply Chat

Including jQuery To Page

Where are 3 ways to use jQuery:

Way 1. Find a direct link to hosted jQuery and add it to page’s <head>.

For example, Google Developers has such link for latest, 2.1.4, jQuery version.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

This link can be used in HTTP- and HTTPS-websites, because it uses https:// protocol.

This link’s file name is “jquery.min.js”, where “.min” postfix means that this is minified version of jQuery:

JavaScript minification is recommended for production, because minified JS has smaller size (loading faster, especially for slow internet connection).

We also can minify all our scripts using online JS obfuscators.

Way 2. Download a jQuery file from its official page (available compressed & oncompressed versions), upload it onto your server and use link from your server, i.e for “libs” folder.

<script src="/libs/jquery.min.js"></script>

Way 2.1. Add it using Bower (package manager for Node.JS) or another package manager.


Once we included jQuery to page, we can start to use it.

At first, let’s improve all operations with DOM model.

Modify DOM Model Via jQuery

1. Obviously, the main feature of jQuery is the queries. Let’s start from them.

Go to last script block and change this:

var divComments = document.getElementById('comments');

…to this:

var divComments = $('#comments');

2. Next, let’s update the operations with innerHTML. Change this:

if (xhr.responseText !== divComments.innerHTML) {
  divComments.innerHTML = xhr.responseText;

…to this:

if (xhr.responseText !== divComments.html()) {
  divComments.html(xhr.responseText);

3. Finally, let’s update the autoscrolling… Oh, there are no scrollHeight wrapper in jQuery! This is the case where we should use both jQuery and raw JS. Change this:

divComments.scrollTop = divComments.scrollHeight;

…to this:

divComments.scrollTop(divComments.get(0).scrollHeight);

Note: divComments.get(0) is equivalent to divComments[0]. It is a “gate” from jQuery to JS. jQuery’s selector always returns an array of HtmlElements, and [0] or get(0) returns the first (or single – in case with id selector) native item.

4. Now all works.

AJAX HTTP GET Requests Via jQuery

Other important feature of jQuery is wrapping of XHR (in asynchronous mode).

Let’s test it. Let’s change this:

<script type="text/javascript">

var divComments = $('#comments');
  
function loadComments() {
  var xhr = getXmlHttp();
  xhr.open('GET', '/getcomments.php', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        if (xhr.responseText !== divComments.html()) {
          divComments.html(xhr.responseText);
          
          divComments.scrollTop(divComments.get(0).scrollHeight);
        }
      }
    }
  };
  xhr.send(null);
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

… to this:

<script type="text/javascript">

var divComments = $('#comments');
  
function loadComments() {
  $.ajax({
    url: "/getcomments.php"
  }).done(function(data) {
    if (data !== divComments.html()) {
      divComments.html(data);
      
      divComments.scrollTop(divComments.get(0).scrollHeight);
    }
  });
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

All works again. AJAX is case where jQuery advantages are on hand. But this is only the beginning…

At first, let’s look at it from lower level. Let’s open a HTTP-sniffer and compare request from raw JS and from jQuery:

Raw XHR:

GET http://localhost/getcomments.php HTTP/1.1
Host: localhost
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 u01-04
Accept: */*
Referer: http://localhost/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4

jQuery:

GET http://localhost/getcomments.php HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 u01-04
Referer: http://localhost/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4

As we can see, jQuery by default adds the “X-Requested-With: XMLHttpRequest” header. It can be used in getcomments.php to make out which request was got via jQuery AJAX, and which was got without it.

 

…But, of course, our code isn’t the shortest way! More shorter equivalent is this:

<script type="text/javascript">

var divComments = $('#comments');
  
function loadComments() {
  $.get("/getcomments.php", function(data) {
    if (data !== divComments.html()) {
      divComments.html(data);
      
      divComments.scrollTop(divComments.get(0).scrollHeight);
    }
  });
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

Also, jQuery has even more hi-level method .load(), but it not compares the div’s html with response and re-sets it anyway, which is worser in performance. And this is a case where too hi-level way is worser than more low-level.

Submit Form Via AJAX Using jQuery

Same as with GET-requests, we can send POST-requests via low-level $.ajax() method, or via hi-level (“shorthand”) method – $.post().

Let’s directly move to hi-level method. Let’s change this:

<script type="text/javascript">

function postComment(nick, text) {
  var xhr = getXmlHttp();
  xhr.open('POST', '/post.php', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */
      }
    }
  };
  xhr.send('nick=' + nick + '&text=' + text);
};

</script>

…to this:

<script type="text/javascript">

function postComment(nick, text) {
  $.post("/post.php", {
    nick: nick,
    text: text
  }).done(function(data) {
    /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */
  });
};

</script>

Let’s also compare it in HTTP-sniffer:

Raw XHR:

POST http://localhost/post.php HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 19
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 u01-04
Content-Type: application/x-www-form-urlencoded
Accept: */*
Referer: http://localhost/
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4

nick=test&text=test

jQuery:

POST http://localhost/post.php HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 19
Accept: */*
Origin: http://localhost
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36 u01-04
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost/
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4

nick=test&text=test

As we can see, jQuery automatically added the “Content-Type: application/x-www-form-urlencoded” header, and converted the content to UTF-8 encoding.

Also, same as with GET-requests, in POST-requests it adds the “X-Requested-With: XMLHttpRequest” header.

But, this code, probably, has even more hi-level equivalent! This is screenshot from jQuery docs:

It says that $.post() method also can directly serialize the form that shortens & simplifies the code.

Let’s try change all this:

<script type="text/javascript">

function postComment(nick, text) {
  $.post("/post.php", {
    nick: nick,
    text: text
  }).done(function(data) {
    /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */
  });
};

</script>

<!-- Post Comment Form -->
<form style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

</div>

…to just this:

<!-- Post Comment Form -->
<form style="height: 0; display: table-row;" onsubmit="$.post('/post.php', $(this).serialize()); return false;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

Fantastic! It really works, not bad, than way with addComment() function!

Note: the $() function returns the jQuery shell for the element:

For example, we getting raw, native HtmlElement:

…converting it to jQuery Object via $() function:

…and now we have access to all useful jQuery functions via this object:

It can really help in partially porting the big JS code to jQuery.

Meanwhile, our porting is done, and we can remove the block with getXmlHttp(), verify if code not contains other raw-JS-garbage which now not needed, and continue our work..

Look bellow – it is our result front-end code. Now you can compare it with our first “simply AJAX” code, and make your conclusion – is “simply” AJAX without jQuery really so simply, for an experienced developer?!

<html>
<head>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

</head>
<body>

<style>

/* All CSS is very simplified. I provide it for example and no more.
 * In Chrome it works tolerably, but in IE and Firefox it works very poorly.
 */

.content {
  display: table;
  
  width: 50%;
  min-width: 400px;
  height: 80%;
  
  /* Center horizontally and vertically */
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  margin: auto;
  
  /* Design */
  border: 1px solid;
  background-color: silver;
  padding: 5px;
}

/* For mobile devices */
@media (max-width: 400px) {
  .content {
    width: 100%;
    min-width: 0;
    padding: 0px;
  }
}
  
</style>

<div class="content">

<!-- Comments List -->
<div id="comments" style="overflow-y: scroll; height: 100%;">

</div>

<!-- Post Comment Form -->
<form style="height: 0; display: table-row;" onsubmit="$.post('/post.php', $(this).serialize()); return false;">
  Nick:<br>
  <input type="text" name="nick" style="width: 100%;"></input><br>
  <br>
  Text:<br>
  <textarea name="text" style="width: 100%;"></textarea><br>
  <br>
  <input value="Submit" type="submit"></input>
</form>

</div>

<script type="text/javascript">

var divComments = $('#comments');
  
function loadComments() {
  $.get("/getcomments.php", function(data) {
    if (data !== divComments.html()) {
      divComments.html(data);
      
      divComments.scrollTop(divComments.get(0).scrollHeight);
    }
  });
};
  
loadComments();  
setInterval(loadComments, 1000)

</script>

</body>
</html>

And, what is important, back-end – getcomments.php and post.php – remained the same compatible, despite all made changes in front-end.

Writing Desktop & Mobile & Web Clients For Our Simply Chat

How To Write Clients

At first, we should understand and note that we already have written a some client. Look at our front-end! It is fully real HTTP-client for our back-end:

Implementing of mobile client is just implementing the same logics for other environment (PL syntax, platform, IDE).

And there is one more condition: if we using localhost, it’s better to upload our server to any dedicated hosting. Yes, you can easily connect to localhost from the same machine where local server runs. But if we want connect from other machine (i.e. from mobile device, or from mobile emulator), this barrier creates additional difficulties. It is much easier to just get around this with a hosting.