6.1. JavaScript: The Big Picture

JavaScript had to “look like Java” only less so—be Java’s dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days or something worse than JavaScript would have happened.

—Brendan Eich, creator of JavaScript

Despite its name, JavaScript is unrelated to Java: LiveScript, the original name chosen by Netscape Communications Corp., was changed to JavaScript to capitalize on Java’s popularity. Brendan Eich, creator of JavaScript, originally proposed embedding Scheme in the browser (Seibel 2009). Although pressure to create a Java-like syntax prevailed, many Scheme ideas survive in JavaScript, including higher-order functions (HOFs), which take functions as arguments and/or produce a function as a return value, which make possible AJAX programming and the Jasmine TDD tool.

Indeed, JavaScript has almost nothing in common with Java except superficial syntax, and is actually much more similar to Ruby. Its dynamic type system is similar to Ruby’s and plays a similarly prominent role in how the language is used. If you have a solid grasp of these concepts from Chapters 2 and 8, and are comfortable using CSS selectors as you did in Chapters 3 and 7, getting started with JavaScript will be easy.

JavaScript has acquired a bad reputation that isn’t entirely deserved. It began as a language intended to allow Web browsers to run simple client-side code to validate form inputs, animate page elements, or communicate with Java applets. Inexperienced programmers be- gan to copy-and-paste simple JavaScript examples to achieve appealing visual effects, albeit with terrible programming practices, giving the language itself a bad reputation. This is not to say the language has no quirks or pitfalls, but it is certainly possible to use it well.

Nonetheless, even when used well, JavaScript still presents a fundamental tension for SaaS. While JavaScript is definitively the language of client-side code, there are many languages for creating the server side. Thus when creating SaaS we must either find ways to integrate two (or more) languages in the same app, as we do in this chapter, or commit to creating both the front and back ends in a single language, as is done when using server-side JavaScript frameworks such as Node or Express.

While it seems appealing to simply pick one language, it is a decision your authors believe to be on the wrong side of history. Few modern complex software systems are written entirely in a single language simply because different languages solve different problems well. For example, the routing layer of Heroku, which receives and distributes incoming traffic from SaaS clients, is written in Erlang, a somewhat obscure language developed for programming highly-reliable telecommunications switches. Erlang is so good at simplifying the creation of code to manages event-driven tasks like handling multiple simultaneous connections that it is worth specializing the routing layer in this way. This philosophy is consistent with that of microservices: each service should be engineered to optimize its focused set of tasks, and as long as the external API stays the same, the implementation language matters little and can even be changed without harming interoperability. Heroku apps have access to shared or dedicated databases using PostgreSQL and Redis (both written mostly in C) and provides runtime support for apps written in a variety of languages. Systems that combine languages are more challenging to develop because of the need to pass information between subsys- tems in different languages in a disciplined way. Nonetheless, your authors believe that such “polyglot” (multilanguage) systems are the future, and managing such challenges is part of the domain of software engineering.

Therefore, we concentrate primarily on the use of JavaScript for SaaS clients, showing how the above problems are addressed with respect to Rails as a specific example. We can differentiate four major approaches, which we list in order of “least JavaScript-intensive” to “most JavaScript-intensive”:

  1. Adding JavaScript to HTML and CSS to enhance the user experience of server-centric SaaS apps. In this scenario, the user experience is that the app consists of a set of different HTML pages, some of which are JavaScript-enhanced. JavaScript requests.

  2. Creating single-page applications (SPAs) in which the user experience is that once the initial page is loaded, no further page reloads or redraws occur, although elements on the page are updated continuously in response to communication with the server. In this scenario, the server appears to the app as one or several service endpoints that return data—most commonly encoded as JSON or XML, though in principle any data format is possible.

  3. Writingfullclient-sideapplicationssuchasGoogleDocs,comparableincomplexityto desktop apps and possibly able to operate while disconnected from the Internet. Like all complex software, such apps reflect some underlying architecture and are increasingly built using JavaScript frameworks that support that architecture, such as Model-View- ViewModel (Vue), Model-View-Controller (Angular), and others.

  4. Creating full server-side apps similar to those we’ve been building using Rails, but using JavaScript frameworks such as Node.js.

In this chapter we focus on cases 1 and 2. But how to choose among the ever-expanding selection of purely front-end JavaScript frameworks? Recall from Chapters 3 and 4 the ben- efits of choosing a framework whose model is a good fit for your app versus the pain of fighting a framework that is a poor fit. Rails is highly opinionated, and if the app you’re trying to build matches Rails’ MVC architecture, its facilities save you a lot of work. The same lesson applies to front-end frameworks, which we can classify on a spectrum from incremental to opinionated. Roughly speaking, an incremental framework is one in you can selectively use for parts of the front end of your SaaS app without having to commit the entire front end to a very specific (opinionated) app structure. For example, React is primarily a reactive view framework that you can choose to use for some but not all of your app’s views. However, most React components in practice rely on JSX , an XML-like extension to JavaScript’s syntax. The Vue view framework does not, making it perhaps easier to incrementally incorporate into projects that otherwise have no need for JSX. In contrast to both React and Vue, Angular and Ember are full Model–View–Controller frameworks for the front end, and it is difficult to combine them with other (non-MVC) structures, so using them represents a serious commitment to (re)structuring the app’s front end.

We will stake out a conservative position in this introduction and introduce enhancing SaaS with unobtrusive JavaScript and the jQuery library. In general, unobtrusive JavaScript emphasizes:

  • Separation of HTML markup (content) from JavaScript (behavior): JavaScript mixed into HTML pages (as was sadly common for early SaaS apps) is hard to read and maintain. JavaScript should instead reside in separate files, using event handlers to “tie” JavaScript code to page elements, as Section 6.7 describes.

  • Progressive enhancement: While JavaScript may enhance aspects of the site’s user experience, the site should remain accessible to users with disabilities (Section 6.5) and users whose browsers have compatibility issues related to the JavaScript Application Programming Interface (JSAPI), the browser functionality that lets JavaScript code manipulate the content of the current HTML page. Both conditions can be largely but not completely addressed by using JavaScript libraries such as jQuery (Section 6.4).

6.1
Figure 6.1: The correspondence between our exposition of server-side programming with Ruby and Rails and client-side programming with JavaScript continues our focus on productively creating DRY, concise code that is well covered by tests

Testing JavaScript is particularly important since most browsers do not display user-visible error messages as a result of JavaScript bugs; instead, the site often simply refuses to work (“silent failure”), or the UI freezes, or the user sees a confusing error message result- ing from a cascading failure rather than directly describing the actual failure. As we will see, integration-level testing of JavaScript-enhanced SaaS apps is straightforward and requires few or no changes to your existing skills using Cucumber and Capybara, but unit-testing of JavaScript is somewhat trickier.

The rest of this chapter introduces the JavaScript language and jQuery framework and how they interact with Rails. Figure 6.1 compares our exposition of server-side and client-side programming. Screencast 6.1.1 demonstrates the two JavaScript features we will add to RottenPotatoes in this chapter.

Section 6.2 introduces the language and how code is connected to Web pages and Section 6.3 describes how its functions work, an understanding of which is the basis of writing clean and unobtrusive JavaScript code. Section 6.4 introduces jQuery3, which overlays the separate browsers’ incompatible JSAPIs with a single API that works across all browsers. Section 6.5 discusses how the use of JavaScript to manipulate the DOM intersects with considerations of accessibility for Web users with disabilities. Section 6.6 describes how jQuery’s features make it easy to program interactions between page elements and JavaScript code, setting the stage for introducing AJAX in Section 6.7. In 1998, Internet Explorer 5 in- troduced a new mechanism that allowed JavaScript code to communicate with a SaaS server after a page had been loaded, and use information from the server to update the page “in place” without the user having to reload a new page. Other browsers quickly copied the technology. Developer Jesse James Garrett coined the term AJAX , for Asynchronous JavaScript And XML, to describe how the combination of this technology to power impressive “Web 2.0” apps like Google Maps.

Testing client-side JavaScript is challenging because browsers will fail silently when an error occurs rather than displaying JavaScript error messages to unsuspecting users. Fortunately, the Jasmine TDD framework will help you test your code, as Section 6.8 describes.

Finally, Section 6.10 describes the mechanisms for both developing and testing browser-based single-page apps (SPAs), which are becoming increasingly popular.

Self-Check 6.1.1. With respect to the challenges of combining multiple languages in one software system, which challenge do you think the JSON data format (introduced in Section 3.6) addresses?

JSON provides a language-independent way to represent hierarchical data structures consisting of basic types such as numbers, strings, Booleans, hash maps (dicts), and arrays. Since virtually all modern languages support these basic types, languages can easily convert between JSON and their own internal representations of these types, allowing for data interchange among them.