11. JavaScript and jQuery

Author:Peter Parente
Builds-on:JavaScript and NodeJS; HTML, CSS, and Bootstrap

11.1. Goals

  • Learn about the browser DOM
  • Learn about AJAX
  • Know what jQuery is
  • Understand the history of jQuery
  • Practice traversing and manipulating the DOM with jQuery
  • Practice invoking REST APIs with jQuery
  • Use the basic panels of the Chrome Developer Tools

11.2. Introduction

jQuery is a library that makes writing client-side JavaScript easier than using the standardi HTML document object model (DOM). jQuery presents a simple API for accessing, navigating, and manipulating HTML; handling web page events; and communicating with web servers, all in a manner consistent across web browsers. Although jQuery arose nearly a decade ago, long before HTML5 and wholesale support for Web standards, it remains at the foundation of many web sites, web applications, and web libraries today.

To get started, watch the jQuery slidecast (~25 minutes) introducing the primary features of jQuery. The slidecast includes demos of the following:

If time permits, review these additional pages:

11.3. Exercises

You will need to complete the Setting Up instructions before you proceed with these exercises. Once you are set up, SSH into tottbox using the vagrant ssh command from the setup instructions. Then tackle the problems below. Document what you find in a gist and share it with the TotT community later.

11.3.1. Clone the ShortSheet project

I’ve seeded a project called ShortSheet (SS) on GitHub at https://github.com/parente/tott-shortsheet. It is a starting point for building a basic spreadsheet web application. We will build out this application throughout the following exercises.

To get started, clone SS to your tottbox shared folder. Start a bash session on tottbox, change to the project directory, and run make vendor. The build will download dependencies of the project, namely Bottle, jQuery, and Bootstrap. When the build completes, run make server in the same directory. Then visit in your browser.

Spend a few minutes looking at the application UI and its structure on disk. Open the Makefile in the root of the project and look at its contents. What did make vendor and make server do? Open the public/index.html file. What does it contain? How about the public/vendor folder? The public/js/shortsheet.js? Share what you find.

11.3.2. Add CSV file import

Right now, the SS shows a sample set of data and nothing more. An Import button exists at the top of the page. Clicking it shows a file picker. Selecting a file causes no change. Yet.

Open the shortsheet.js file. Find the TODO comment about supporting import. Add code using jQuery that attaches a change event handler to the import-csv' file input element and invokes the function mentioned in the comment. (Hint: Look in the jQuery doc.)

Save this second sample CSV file with a .csv extension on your machine. Try importing it into your spreadsheet. The spreadsheet should render the new data if your code is working properly.

If you hit problems, use the Chrome Developer Tools (or equivalent in your browser of choice) to debug the problem. (Hint: Adding simple console.log statements to your code and looking for their output in the developer tools Console tab can go a long way.)

11.3.3. Make cells editable

The SS cells are read-only at the moment. We want to make them editable so that you can change values and, later, enter formulas.

Look for the TODO comment about making cells editable in the shortsheet.js file. Add code using jQuery that listens for double click events on all cells in the spreadsheet table (i.e., all <td> HTML elements in the <tbody> element). In the event listener function, set the contenteditable attribute on the clicked table cell to true. Then give the cell keyboard focus. (Hint: Again, use the jQuery doc or Google.)

If your code is working properly, the browser will highlight the cell, show a text caret in it, and let you edit its content.

11.3.4. Make cells read-only again

Allowing edits to a cell is only half the battle. You must also add code to take the cell out of edit mode when you want to cease editing. There are many ways you might do so. Handle two of them for now.

  1. If you double click another cell while in editing mode, the current cell should become read-only and the newly clicked cell should be editable.
  2. If the you press the Enter key, the current cell should become read-only.

Again, use jQuery to set event handlers for these conditions. Tracking which cell is currently in editing mode in a variable might help in resetting it.

11.3.5. Support adding rows and columns

The spreadsheet is still pretty static at the moment. The row and column count is fixed at the dimensions of the data you loaded. Add UI to allow addition or removal of rows and columns. Add the appropriate jQuery event handlers to monitor for these elements. When an event occurs, add the appropriate HTML elements:

  • For a row, add a <tr> containing a number of <td> elements equal to the current number of columns.
  • For a column, append a <td> element to each row <tr> element currently in the table.

Start by supporting additions at the end of the last row or column. Once you have that code working, consider changing the UI and code to support additions anywhere in the sheet.

11.3.6. Support cell formulas

All spreadsheets have support for formulas. Think about a syntax for arithmetic operations in ShortSheet. Maybe a subset of JavaScript? Maybe something custom? Should it support individual cells? Cell ranges?

Add code to shortsheet.js to parse and execute formulas when a cell changes from editable to read-only. Store the formula in a data-formula attribute on the cell. Parse and execute the formula. Store the result of the formula in the cell itself.

Re-evaluate any formulas in the sheet whenever a new row or column is added. Change the CSV loading code to add any formulas present in the CSV as data-formula attributes and evaluate them all.

Consider editing the sample.csv file to include a few formulas to test your code.

11.3.7. Think about your design

Take a moment and think about the data model of SS. What happens when you want to implement support for saving a spreadsheet? How would you gather up the formulas and plain, old values? Does storing everything in the HTML make things hard in the long-term? (Hint: This is the topic we’ll address in the MV* and Backbone session.

11.3.8. Support row and column removal

Add UI and code for removing entire rows and columns from the spreadsheet. Remember to re-execute any formulas after adding either. (Hint: Have you put the code for formula execution in its own reusable function yet?)

11.3.9. Add CSV URL import

Looking back, it’s silly that you had to download a CSV file from a GitHub Gist just to load it from your local machine into your web browser. Why not just fetch it directly from the Gist URL?

One complicating factor is that JavaScript running in a web browser can only send requests to the same origin that served up the HTML page that includes it. This security precaution is known as the same origin policy and is meant to prevent cross-site scripting attacks. Web applications have ways of working around this limitation, one of is to simply make such requests on the server side, not the client-side.

The Python web server hosting the SS web assets already has a /gist/:userid/:gistid resource. Sending an HTTP GET request to this resource with a valid GitHub username and Gist ID will cause the server to respond with the raw text of the Gist.

Add elements to the ShortSheet UI to collect this information, and a trigger to send it to the Python server. Add jQuery code to listen for the trigger event and to send a GET request (AJAX request) with the requisite information. Populate the spreadsheet with the response CSV in the same manner as when the file existed locally.

Test your code with the gist you downloaded previously with user ID parente and gist ID 7965617. Or choose another CSV gist located on GitHub as a test.

11.3.10. Add more features

Consider other features most spreadsheets have (or don’t have). Implement whatever you wish. Here are some starting ideas.

  • Show errors loading spreadsheets, evaluating formulas, and so on using Bootstrap alerts.
  • Support column and row sorting by value.
  • Support column and row re-ordering via drag and drop.
  • Support keyboard navigation of the sheet.
  • Support more formula operations.
  • Support progressive loading of large CSV files.
  • Set columns to a fixed, but adjustable, width.
  • Allow users to download modified sheets as CSV files.
  • Add spreadsheet persistence on the server side.
  • Make sheet display more attractive with better styling.
  • Show a busy spinner while loading data.

11.4. Projects

If you want to try your hand at something larger than an exercise, consider one of the following.

11.4.1. Stateless Book Builder

See Gary’s Stateless Server Idea blog post.

11.4.2. Slidecast Framework

Pete hacked together a little JS module for reveal.js to support the self-narrating slidecasts you see on the TotT session pages. Extract this code out of the TotT GitHub repository and migrate it to its own repo. Then spend some time cleaning it up, making it more general purpose, and documenting it so others can use it to build their own slidecasts.

11.4.3. Hosted Slidecasts

Take the slidecast framework mentioned above and build a cloud-hosted version. One approach could be:

  1. A user signs in.
  2. The user links her slidecast account to her DropBox account.
  3. The user enters Markdown to construct her slides.
  4. The user records audio right on the site via the HTML5 getUserMedia API.
  5. The site persists the slideshow in the user’s DropBox account.

This project would be a large undertaking, but unique on the web at the moment, as best as I can tell.

11.4.4. Improve the IPython Notebook UI

jtyberg writes:

I love IPython notebook for ad-hoc analysis. However, there are a few shortcomings of the web UI that lessen my user experience. Among them is the tedious nature of reordering cells (moving them up or down) within a notebook. I would like to be able to select multiple cells and move them up/down the page all at once.

A possible solution would be to enable grouping of cells. Can we modify the underlying DOM structure by adding cell elements into the same parent? Then we can manipulate the parent element.

Another idea would be a gutter view within the notebook that shows a condensed view of the notebook content (think Sublime text editor). What if we could select individual cells or cell groups and move them up/down the page by dragging and dropping from within the gutter? That would be sweet.

The IPython Notebook has an unstable but working JavaScript API that might be useful in accomplishing either or both of these.

11.5. References

Learn jQuery
Explanations, workarounds, best practices, how-tos
Chrome Developer Tools Documentation
Official documentation from Google