Skip to content

Latest commit

 

History

History
 
 

05-client-jquery-ajax

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Lesson 5 - The Client, jQuery, and AJAX

Web Applications handle much of their database communication and logic on the server, but the front-end is what the client actually sees and interacts with. Without a clean, intuitive, responsive, and enjoyable client-side, the best server application in the world wouldn't be much.

Today we are going to learn the basics of building and manipulating the client and building communication routes between the client and server.

Development Console

One of the most important and powerful tools in web development is the development console. All modern browsers have some sort of console available, but the Chrome console is what we will use. Its a great and common browser and very developer friendly.

Browsers

As a quick aside, all browsers are different. They have gotten much closer in recent years (HTML5 browsers), but you will still run into some differences in how your sites look and function due to these differences. It's worth understanding the different browsers with which people may view your site you so can choose what to optimize:

Some key trends to note include the rise of Chrome as well as the fall of IE and Firefox. Also worth noting is the rapid growth of mobile browsing (pink). We won't address mobile web design in this class, but it is definitely something to consider before launching a new site.

Ok, back to the console. On any webpage, right-click, and select Inspect Element. You'll see something like this pop up in the bottom of the window: devconsole The dev console contains a ton of features (seriously, there are more features than I've even heard of). We'll start with the basic tabs that you use most often:

  • Elements: This is a collapsable and editable view of the HTML on the current page. You can double-click to edit fields or DOM nodes. On the right side is a panel showing the CSS styling applied to the element you are highlighting. Again, double-click to edit. A useful shortcut here is the magnifying glass in the top-left corner of the console. Click that and then click in the browser somewhere to jump to the HTML of that element.
  • Network: While you're in this tab, refresh the browser. The tab will list all the requests that the web page makes, including the size, timing, and specific response. This can be useful for debugging your server's responses or other API's.
  • Sources: The holy grail of JavaScript debugging tools. Set breakpoints, step through code, all the fun stuff that an IDE would give you. We'll get back to this when we go over debugging in more depth.
  • Console: A full JavaScript console that operates in the current scope of the Javascript on the page (including inside breakpoints). Run commands, and read through your client-side console.logs in this window. Server-side javascript console.log statements will log in your terminal. When you have console.log statements in client-side javascript (which we'll discuss more below), they will show up in your developer console. As a bonus, if you click the "console icon" console icon in the upper-right corner, the console will pop up below whatever other tab you want to reference.

###Javascript Code in Console With the developer console, you can actually interface and inject your own javascript into a webpage. If you open up the console (as described in last bullet point above) you can type in your own commands and have them execute. Try typing in:

alert('hi there')

Not only that, you can also see their javascript files and even have access to any variables. Where you find those is under the sources tab in the console. Follow along and pull up their files as shown in the screenshot:

Cookie Clicker

If you investigate (or just guess) you will notice a variable called "Game" and if you start to type that into the console it will even suggest an autocomplete for it. You can look at its functions and realize that there is a variable called cookiePs, which you might guess corresponds to cookies per second. You can set cookiesPs to any value and see what happens:

Cookie Clicker hacked

The takeaway from all this is that the client is NOT secure. A smart user can take your app apart on the client and do anything he wants with it. All security needs to be implemented on the server side so that it cannot be meddled with.

HTML Reprise

We've seen some HTML before but let's go through a more formal overview of it. HTML is a markup language that uses tagged elements to describe the structure of a webpage. Elements are specified with start and end tags which enclose their content, including children elements (<div><p>Hi again</p></div>). The p element is a child of the div element. Some elements cannot have content or children and do not have an end tag. Instead their start tag ends with a slash (<br/>) While HTML can include presentation specifications, it is highly recommended to use HTML for structure and Cascading Style Sheets (CSS) to define the look and layout of the page. There are many HTML tags, but you will mostly use a small number (click the link to see the official documentation for the tags):

  • <a>: The Anchor element defines a hyperlink like those seen in blue throughout this document.
  • <article>: Articles are used to signify a self-contained composition within a page, ei. forum post, documentation entry, newspaper article, etc.
  • <body>: The body tag represents the content of an HTML document. There can be only one.
  • <canvas>: A frame in which you can draw graphics through JavaScript. This can be images and shapes or some pretty crazy applications.
  • <div>: Arguably the most common element, the Division Element is a generic HTML container element. It doesn't inherently represent anything, but it can also be used (through CSS) to represent pretty much anything. Usually it is used to group elements or for style. Ideally it should not be used when a semantic element like <article> or <nav> is appropriate, but this is not widely adhered to.
  • <form>: Defines a section of interactive controls and inputs that will submit information to a server. These will usually include a number of <input> tags.
  • <h1>: Header text. Pretty common, there are six tags h1 - h6. Use these rather than a <p> with a css class header-1 or something like that.
  • <img>: Display an image with the image tag. <img> tags cannot have children and do not have an end tag (<img source="./foo.jpg" />).
  • <ul>, <ol>, and <li>: Un-ordered list, ordered list, and list element tags are used for structuring list content in HTML.
  • <p>: The paragraph element defines a block of text.
  • <span>: The span element is like a div in that it is a generic container element. However, in contrast, spans are inline elements by default (they do not break the flow of elements on the page) and are often used to style text: <p><span class="fancy-text">Some text</span></p>.
  • <table>: Prior to the use of CSS, the table element, along with <th> (table header), <tr> (table row), and <td> (table data), were often used as a method for page layout. Please don't do this. They can still be used for the categorization of data, but the styling and layout or your pages should all be controlled by CSS.

Check out this site or Google "HTML layout tips" to read more about formatting techniques.

Experimenting?

You've seen JSFiddle and CodePen in action, remember that you can use them to test out, share, and see the output of snippets of HTML, CSS, and JavaScript.

HTML Forms

In the last homework, we passed information to the server through the URL. This required us to do everything as a GET request, which is BAD, and was pretty limiting. Of course it was also a pretty atrocious User Experience (UX).

GET /cats/new
GET /cats
GET /cats/bycolor/:color
GET /cats/delete/old

Now we're going to look at a better way to do this using HTML forms.

To start we'll need a basic Express app again. You can start a new app or modify your homework, but we'd recommend starting from the expressintro app from class 03. In your terminal, cd into your your /lessons/05-client-jquery-ajax/ folder, use this command to copy last class's app over:

cp -r $PATH-TO-OLINJS-FOLDER/lessons/03-express-templates/expressintro ./::yourAppName::

Replace ::formsApp:: with whatever you want to name your forms app.

Replace the contents of home.handlebars with:

<h1>Forms and Stuff</h1>

<h2>GET request</h2>
<form action="getCat" method="GET">
  Name: <input type="text" name="name"/><br/>
  <input type="radio" name="mood" value="happy" checked/>Happy<br/>
  <input type="radio" name="mood" value="grumpy"/>Grumpy<br/>
  <input type="submit" value="Submit">
</form>

<h2>POST request</h2>
<form action="getCat" method="POST">
  Name: <input type="text" name="name"/><br/>
  <input type="radio" name="mood" value="happy" checked/>Happy<br/>
  <input type="radio" name="mood" value="grumpy"/>Grumpy<br/>
  <input type="submit" value="Submit">
</form>

<h2>AJAX request</h2>
<form id="ajax-form" action="getCat" method="POST">
  Name: <input type="text" name="name"/><br/>
  <input type="radio" name="mood" value="happy" checked/>Happy<br/>
  <input type="radio" name="mood" value="grumpy"/>Grumpy<br/>
  <input type="submit" value="Submit">
</form>
<div id="result">
</div>

This may seem like a very redundant page. That's because it is. We are creating 3 forms to walk through some of the differences in how we can communicate between the client and server. If you run the app and navigate to http://localhost:3000, you should see this: Forms If you fill out the first form and press submit you will get a 404 error and the URL will change to something like: http://localhost:3000/getCat?name=bob&mood=happy.

If you fill out the second form you will get a 404 error and the url will change to: http://localhost:3000/getCat.

We are submitting the form information to the server in two different ways, GET and POST, as specified by the method attribute of the opening <form> tag. The action attribute specifies the URL.

Now let's hook up the routing to make these forms actually do something.

Create a new file in the routes directory named getCat.js and put the following in it:

var routes = {};

routes.getCatGET = function(req, res) {
  console.log(req.query);
  res.end(".");
};

routes.getCatPOST = function(req, res) {
  console.log(req.body);
  res.end(".");
};

module.exports = routes;

All we are doing here is printing the form data to the console. The line res.end(".") sends a dot back to the client. If you forget to send a response for a route, the browser will keep waiting until the request times out.

You'll note that for the GET request, we look at the req.query object into which Express parses the URL query string. For the POST request we look at req.body where the body-parser middleware has parsed the POST parameters.

Now that you've created functions which handle the get and post requests from your forms, you need to tell the app to use them. Open app.js and add the following line somewhere near the rest of your require statements:

var getCat = require('./routes/getCat.js');

and these two lines with the rest of the express routing:

app.get('/getCat', getCat.getCatGET);
app.post('/getCat', getCat.getCatPOST);

If you now submit the first two forms again you can see how Express parses the data:

{ name: 'bob', mood: 'happy' }
GET /getCat?name=bob&mood=happy 200 0.876 ms - -
{ name: 'alice', mood: 'grumpy' }
POST /getCat 200 0.538 ms - -

Both the POST and GET request parameters are stored in the same format. That's convenient as it will allow us to use both parameters in the same way.

Replace the contents of getCat.js with:

var path = require('path');

var routes = {};

var getCatImage = function(catParams) {
  var imageLocation;
  switch (catParams.mood) {
    case "happy":
      imageLocation = path.join(__dirname,"../public/images/cat.jpg");
      break;
    case "grumpy":
      imageLocation = path.join(__dirname, "../public/images/grumpy.jpeg");
      break;
  }
  return imageLocation;
}

routes.getCatGET = function(req, res) {
  console.log(req.query);
  res.sendFile(getCatImage(req.query));
};

routes.getCatPOST = function(req, res) {
  console.log(req.body);
  res.sendFile(getCatImage(req.body));
};

module.exports = routes;

Go ahead and grab the two images from the formsAppWalkthrough/public/images directory and copy them into yours. Now if you fill out either of the first two forms, you will get the appropriate cat.

As we've discussed before, your uses of different request methods can vary, but for a purpose like this, sending innocuous information as part of a non-modifying query, a GET request is your best bet. However, if you're making a login form, with private or personal information like passwords, you're going to want to go with POST so that the password is not exposed in the URL or stored in the server logs or your browser's history.

To make the third form work, we need to learn about client side JavaScript and AJAX requests. First, we'll play around with client-side JS to see how it works, then learn about what AJAX requests are, and finally, we'll make that third form work, and display the right cat images WITHOUT A PAGE REFRESH. We'll need to use client-side JavaScript to make our AJAX requests work, so let's get into that first.

Client JavaScript and jQuery

JavaScript is a language. Node (server-side) is a "platform". Your browser (client-side) is also a "platform" that runs client-side JavaScript, with a lot of different capabilities.

What is client-side JS good for? Before, we used JavaScript to serve websites. Now we'll use client-side JS to add interactivity to a page. You used Handlebars in the last lesson to make templates that display content, forms, etc. After a user gets your template, though, they can't do much until they submit a form or go to a new page. With client-side JS, we can make your website much more useful and powerful.

One important caveat: Client-side code knows nothing about your server. For all it cares you could be running Python, Ruby, ALGOL or on a Commodore 64. So when you write JavaScript in Node.js, you can't run the same functions in your web browser. We'll explain later how to make them communicate with each other.

The first step to doing any sort of dynamic actions on your websites is running some JavaScript. You can include JavaScript directly in an HTML document using <script> tags, but writing any significant code within an HTML document would be silly. Instead we will reference a separate .js file in our HTML, similar to how we include CSS.

Create a new file main.js in the public/javascripts directory of your forms app, and paste in the following:

console.log("hello world");

Then add this <script> tag to your main.handlebars layout file (in the views/layouts folder) between the closing </body> and closing </html> tags like so:

</body>
<script type="text/javascript" src="javascripts/main.js"></script>
</html>

When HTML is parsed, it is rendered top-down. When a script tag is hit, its contents are run immediately. This means that if your JavaScript references part of the DOM it may not exist yet and your script will not run as expected. There are some ways to force your script to wait until the page is ready before running, but a simple way around this is to just add your <script> tag below the HTML body.

Now, when you refresh the page, open up the developer console and you should see:

hello world

Being able to print to the console and do math and such is nice, but really we want to interact with the DOM. To do that we need to dig into the document object. In the dev console run:

> document.body.children[1].innerHTML
"GET request"

That returns the contents of the 2nd child of the <body> tag, which in this case is <h2>GET request</h2>.

We can assigned the innerHTML parameter to a new value:

document.body.children[1].innerHTML = "Nothing at all..."

and the webpage will change accordingly.

But that's pretty much the extent to which knowing the structure of the document object is particularly useful. The API of the DOM is a bit dense and not exactly user friendly. Feel free to read more about it here, but for now we are going to let other people do the hard stuff for us and make editing the DOM a lot easier on ourselves. This is where jQuery comes in handy. jQuery is a fairly ubiquitous library that makes DOM manipulation, event handling, animation, and AJAX much easier.

We will include jQuery in the client by adding the line:

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

To the <head> section of main.handlebars. In the case of JavaScript dependencies, we include them early because they are not dependent on the contents of the <body> and it helps to keep them organized and easily findable.

If you refresh the page, nothing will appear different, but you now have a powerful tool at your disposal, the $. You can access the jQuery object in two ways jQuery() or $(), but everyone uses the $.

So now let's change the DOM again:

$("h1").html("That was easy")

Try these guys:

// hide all the inputs
$("input").hide()
// show all the inputs
$("input").show()
// Add some pretty backgrounds
$("h2").css("background", "blue")
// Chain some changes
$("h2").css("background","red").text("headings!")
// Add some content
$('body').append('<img src="http://i.minus.com/iFxelkyarGr5D.gif">');
// And this:
$('*').css('background-image', 'url(http://omfgdogs.com/omfgdogs.gif)');

You can clear any changes you make with jQuery by refreshing to page as that will reload the original HTML without your changes.

jQuery is a very useful library that will allow you to make some dynamic and engaging front-ends to your applications.

The $("") syntax is a jQuery Selector. Within the quotes you can place any CSS style selector to retrieve DOM elements. For instance, "h1" gets all <h1> elements, "#ajax-form" gets the element with the id "ajax-form", and ".left-column" gets all elements with the class "left-column". You'll learn more about these when we get to CSS, but for now just recognize how much much easier it is to manipulate DOM elements when you can search for them using jQuery.

AJAX and XHR Requests

You probably noticed something about how the two first forms act. When you submit them, you are taken to an entirely new page. You might even get a little white flash of a blank screen before the image loads.

Now check out Google: When you search for something the top search bar and Google Plus account information are there the entire time as the bottom, results part of the page reloads. You dont even need to hit submit. If you change your search then stop typing, after a second or so the search results will show up. This is accomplished through a method called Asynchronous Javascript and XML (AJAX) requests.

AJAX is a method of using client-side JavaScript to send XML HTTP Requests (XHR Requests) to a server in the background, without interfering with the display and behavior of the current page. This is the technology that Google and the majority of modern websites use to make their pages seem quicker and more responsive. Using AJAX allows websites to communicate with their servers (or external servers) without making entire new page requests. This also allows them to modify individual sections of HTML instead of replacing the whole page. Now let's use our new client-side JavaScript skills to make AJAX requests and do things without a page refresh!!

AJAX with jQuery

Now let's make the third form work. First off, we are going to modify the getCatGET function so that it responds differently when it receives an AJAX request.

Replace the contents of getCat.js with this:

var path = require('path');

var routes = {};

var getCatImage = function(catParams, absolute) {
  var imageLocation;
  var happyCat = "images/cat.jpg";
  var grumpyCat = "images/grumpy.jpeg";
  switch (catParams.mood) {
    case "happy":
      imageLocation = absolute ? path.join(__dirname,"../public/", happyCat) : happyCat;
      break;
    case "grumpy":
      imageLocation = absolute ? path.join(__dirname, "../public/", grumpyCat) : grumpyCat;
      break;
  }
  return imageLocation;
}

routes.getCatGET = function(req, res) {
  if (req.xhr) {
    res.send(getCatImage(req.query));
  } else {
    res.sendFile(getCatImage(req.query, true));
  }
};

routes.getCatPOST = function(req, res) {
  res.sendFile(getCatImage(req.body, true));
};

module.exports = routes;

The first thing we did is modify getCatImage so that it can return either an absolute link, which Express needs in order to send the image file, or a relative link, which the browser needs in order to send a separate request for the image file. While we originally wrote the getCatImage function for one purpose, by refactoring it a bit we were able to reuse most of the same code for a second purpose.

Secondly, we added a check in the getCatGET function to determine if the request was an XHR request and if so, to return the relative path to the image, instead of the full image file.

Now we need to write some code on the client in order to get the 3rd form to send the XHR request.

Replace the contents of main.js with this:

var $form = $("#ajax-form");

var onSuccess = function(data, status) {
  var img = "<img src='"+data+"'/>";
  $("#result").html(img);
};

var onError = function(data, status) {
  console.log("status", status);
  console.log("error", data);
};

$form.submit(function(event) {
  event.preventDefault();
  var mood = $form.find("[name='mood']:checked").val();
  var name = $form.find("[name='name']").val();
  $.get("getCat", {
    mood: mood,
    name: name
  })
    .done(onSuccess)
    .error(onError);
});

Let's work through this file one step at a time, and if any of this is confusing, please ask about it!!:

  • First we store the jQuery selector for the 3rd form so we don't need to keep searching for it.
  • We define an onSuccess function. This function takes data and status as parameters, which $.get() supplies. It then uses the data returned from the server to create a new <img> tag and add it to the page. and the sets the contents the <div id=result> element to that image.
  • We define an onError function, which prints the status and contents of the error.
  • We add an event handler for the "submit" event to the 3rd form. This means that if that form raises a "submit" event then the callback function will be run with the event object as a parameter.
    • The first thing we do in the callback is event.preventDefault();. This line stops the default action of the event from being triggered. In this case, this is what stops the browser from sending the request and loading a new page.
    • Using some jQuery DOM searching we get the values of the "name" text input and the "mood" radio button.
    • We then initiate an AJAX request using the GET method with $.get(). jQuery similarly has a $.post() function for POST. We pass the two parameters into the GET request which are internally packaged into the same query string we've seen before. Since you're doing a GET request with getCat as the first parameter, it's calling the function getCatGET in your routes\getCat.js file, because in app.js we specified in our API that when we get a GET request for getCat (app.get("/getCat", getCat.getCatGET);) we call the function getCatGET.
    • Finally, we specify what should happen if the request is successful or if it returns with an error. The $.get() function returns a jQuery XHR object ('jqXHR') which implements a Promise interface. Promises are a structure that allow management of lots of callbacks in a simple, easier to read manner. We wont talk too much about promises for now, but the important part is to know that the .done() callback is called when the jqXHR object is completed successfully, the .error() callback is called when there is an error, and the .always() callback (which we did not include here) is called on completion whether successful or not.

Putting all of this together allows us to communicate with the server without sending GET requests from the URL bar and without reloading the page. We can then dynamically change the contents of the DOM without changing the entire page. That is just a taste of the power of client-side JavaScript and AJAX.

##Additional and Alternative learning Resources A free Google-sponsered course on the devtools: https://www.codeschool.com/courses/discover-devtools