B.3 JavaScript

It is way more challenging to briefly introduce JavaScript than HTML and CSS, since it is a programming language. There are many books and tutorials about this language. Anyway, we will try to scratch the surface for R users in this section.

In a nutshell, JavaScript is a language that is typically used to manipulate elements on a web page. An effective way to learn it is through the JavaScript console in the Developer Tools of your web browser (see Figure B.1), because you can interactively type code in the console and execute it, which feels similar to executing R code in the R console (e.g., in RStudio). You may open any web page in your web browser (e.g., https://yihui.org), then open the JavaScript console, and try the code below on any web page:

document.body.style.background = 'orange';

It should change the background color of the page to orange, unless the page has already defined background colors for certain elements.

To effectively use JavaScript, you have to learn both the basic syntax of JavaScript and how to select elements on a page before you can manipulate them. You may partially learn the former from the short JavaScript snippet below:

var x = 1;  // assignments
1 + 2 - 3 * 4 / 5;  // arithmetic

if (x < 2) console.log(x);  // "print" x

var y = [9, 1, 0, 2, 1, 4];  // array

// function
var sum = function(x) {
  var s = 0;
  // a naive way to compute the sum
  for (var i=0; i < x.length; i++) {
    s += x[i];
  }
  return s;
};

sum(y);

var y = "Hello World";
y = y.replace(" ", ", ");  // string manipulation

You may feel the syntax is similar to R to some degree. JavaScript is an object-oriented language, and usually there are several methods that you can apply to an object. The string manipulation above is a typical example of the Object.method() syntax. To know the possible methods on an object, you can type the object name in your JavaScript console followed by a dot, and you should see all candidates.

R users have to be extremely cautious because JavaScript objects are often mutable, meaning that an object could be modified anywhere. Below is a quick example:

var x = {"a": 1, "b": 2};  // like a list in R
var f = function(z) {
  z.a = 100;
};
f(x);
x;  // modified! x.a is 100 now

There are many mature JavaScript libraries that can help you select and manipulate elements on a page, and the most popular one may be jQuery. However, you should know that sometimes you can probably do well enough without these third-party libraries. There are some basic methods to select elements, such as document.getElementById() and document.getElementsByClassName(). For example, you can select all paragraphs using document.querySelectorAll('p').

Next we show a slightly advanced application, in which you will see anonymous functions, selection of elements by HTML tag names, regular expressions, and manipulation of HTML elements.

In Section 2.5.2, we mentioned how to enable MathJax on a Hugo website. The easy part is to include the script MathJax.js via a <script> tag, and there are two hard parts:

  1. How to protect the math content from the Markdown engine (Blackfriday), e.g., we need to make sure underscores in math expressions are not interpreted as <em></em>. This problem only exists in plain Markdown posts, and has been mentioned in Section 1.5 without explaining the solution.

  2. By default, MathJax does not recognize a pair of single dollar signs as the syntax for inline math expressions, but most users are perhaps more comfortable with the syntax $x$ than \(x\).

The easiest solution to the first problem may be adding backticks around math expressions, e.g., `$x_i$`, but the consequence is that the math expression will be rendered in <code></code>, and MathJax ignores <code> tags when looking for math expressions on the page. We can force MathJax to search for math expressions in <code>, but this will still be problematic. For example, someone may want to display inline R code `list$x$y`, and $x$ may be recognized as a math expression. MathJax ignores <code> for good reasons. Even if you do not have such expressions in <code>, you may have some special CSS styles attached to <code>, and these styles will be applied to your math expressions, which can be undesired (e.g., a light gray background).

To solve these problems, I have provided a solution in the JavaScript code at https://yihui.org/js/math-code.js:

(function() {
  var i, text, code, codes = document.getElementsByTagName('code');
  for (i = 0; i < codes.length;) {
    code = codes[i];
    if (code.parentNode.tagName !== 'PRE' &&
        code.childElementCount === 0) {
      text = code.textContent;
      if (/^\$[^$]/.test(text) && /[^$]\$$/.test(text)) {
        text = text.replace(/^\$/, '\\(').replace(/\$$/, '\\)');
        code.textContent = text;
      }
      if (/^\\\((.|\s)+\\\)$/.test(text) ||
          /^\\\[(.|\s)+\\\]$/.test(text) ||
          /^\$(.|\s)+\$$/.test(text) ||
          /^\\begin\{([^}]+)\}(.|\s)+\\end\{[^}]+\}$/.test(text)) {
        code.outerHTML = code.innerHTML;  // remove <code></code>
        continue;
      }
    }
    i++;
  }
})();

It is not a perfect solution, but it should be very rare that you run into problems. This solution identifies possible math expressions in <code>, and strips the <code> tag, e.g., replaces <code>$x$</code> with \(x\). After this script is executed, we load the MathJax script. This way, we do not need to force MathJax to search for math expressions in <code> tags, and your math expressions will not inherit any styles from <code>. The JavaScript code above is not too long, and should be self-explanatory. The trickiest part is i++. I will leave it to readers to figure out why the for loop is not the usual form for (i = 0; i < codes.length; i++). It took me quite a few minutes to realize my mistake when I wrote the loop in the usual form.