This post is part 23 of 36 in the series Programming by Stealth

So far in this series we have been using jQuery to alter existing HTML elements by changing their attributes or style. In this instalment we take things to the next level, and learn how to use jQuery to create entirely new HTML elements, and inject them into the DOM, and hence, into the web page.

We’ll be working towards our first truly practical assignment in the series – a function that finds all links on a page, and if, and only if, they lead to an external page, alters them to open in a new tab, and appends an icon indicating that fact. In order to work up to that we need to learn five new things:

  1. How to build HTML elements with jQuery
  2. How to inject HTML elements into the DOM
  3. How to loop through each element represented by a jQuery object
  4. How to embed images directly into web pages using Data URLs
  5. How to use the 3rd-party library URI.js to interrogate URLs

There are four examples in this instalment, and a starting-point for the challenge. I’ve gathered them, and the other files they depend on, into a ZIP file which you can download here. It’s assumed that you’ll extract this ZIP file and place the five HTML files and one folder it contains into a folder named pbs23 in your local web server’s htdocs folder. The folder is particularly important because it contains a copy of the URI.js library, and if it’s not in the same folder as pbs23d.html and pbs23-assignment.html, those pages won’t work.

Solution to PBS 22 Challenge

But first, below is a solution to the challenge I set at the end of the previous instalment. I provided an HTML file as a starting point, and then set the following tasks:

  1. In the script element within the head element, declare a variable named blinkIntervalID with the value 0 (this variable will be in the global scope)
  2. Below your variable declaration, declare a function named toggleBlinking(). This function will take no arguments, and return nothing. If the current value of the global variable blinkIntervalID is 0, this function should create a new interval which will toggle the class highlighted on all paragraphs every second, and save the interval ID into the global variable blinkIntervalID. If the current value of the global variable blinkIntervalID is not 0, the function should cancel the timeout who’s ID is stored in the global variable blinkIntervalID, and set the variable to 0.
  3. Create an anonymous function that will execute when the DOM has finished loading. Inside this function, add an event handler to every paragraph that will call the function toggleBlinking() every time the user clicks on a paragraph.

Below is my solution to the assignment. Just a reminder that when it comes to programming, there are an infinity of possible correct solutions, so if your code works, but look different to mine, that’s just fine!

Creating HTML Elements with jQuery

We have already learned that jQuery heavily overloads functions – that is to say, the same function behave in different ways depending on what arguments are passed. So far, we know that the $() function implements all the following behaviours when passed different arguments:

  1. A String containing a CSS selector as the only argument – select all matching elements in the entire document. For example, select all links: $('a');
  2. A string containing a CSS selector as the first argument, and a jQuery object as the second argument – search the HTML elements represented by the jQuery object for all matching elements. For example, select all links within paragraphs: $('a', $('p'));
  3. A DOM object as the only argument – convert the DOM object to a jQuery object. For example, $(this) inside jQuery callbacks.
  4. A function object (callback) as the only argument – execute the function when the DOM becomes ready.

We can now add a fifth behaviour to that list – if you pass the $() function a HTML tag as a string, it will construct a new HTML element.

For example, to build a top-level heading with the text a header, you could do the following:

Similarly, I could use jQuery to build a link to my website as follows:

Note that jQuery will accept a full HTML tag with attributes and content all defined within the string. To illustrate this point, the following two lines of code produce the same result:

My preference is for the second style, so that’s what you’ll see me use in examples throughout this series.

When we create HTML elements in this way, they exist, but they are not part of the page. It’s almost like they are in a parallel dimension. For these elements to become visible, they need to be injected into to the DOM. JQuery provides a number of functions for doing this. These functions should be called on a jQuery object representing an existing element, they will then place the new element relative to the existing element in one of the following ways:

.before()
Injects the new element into the DOM directly before the existing element.
.after()
Injects the new element into the DOM directly after the existing element.
.prepend()
Injects the new element into the DOM inside the existing element as the first child element.
.append()
Injects the new element into the DOM inside the existing element as the last child element.

Let’s pause for an example. The HTML page below contains a heading followed by some paragraphs of text. We’ll use jQuery to inject an aside element as the last element within the body element containing the character count for the paragraphs.

We can get the character count for all the paragraphs with the following snippet:

We can create an aside tag with the following snippet:

Finally, we can inject this new element into the end of the body element with the following snippet:

Remember that we need all the above code to run after the DOM becomes ready.

Putting it all together, we get the following full HTML page (pbs23a.html in the ZIP file):

Looping Through a jQuery Object

As we know, jQuery objects represent zero or more HTML elements. It’s easy to apply basic changes to all elements an object represents – we’ve seen this many times already in the previous few instalments. For example, we can turn every paragraph in a page red with the following snippet:

However, sometimes we need to apply similar, but not identical, changes to each element represented by a jQuery object. In these kinds of situations, we need to loop through each element represented by the object one by one. Or – to put it another way, we need to iterate over the elements represented by the jQuery object. This is what jQuery’s .each() function is for. This function expects one argument, a callback, and it will execute that callback once for every element the object represents. Within the callback, the current DOM element will be available via the special this variable. As usual, to convert this DOM object to a jQuery object, pass it to the $() function, i.e. use $(this).

As an example, let’s alter our previous example so it also puts a count at the end of every paragraph.

The following snippet does the work:

Below is a full web page so you can see the snippet in context (pbs23b.html in the ZIP file):

Images Via Data URLs

Usually, a URL points to a location where data can be retrieved from, but, it’s possible to embed data directly into a URL using so-called data URLs. The part of a URL before the first : character is known as the URL’s scheme. We’re used to seeing the http, https, and perhaps ftp schemes, but there is also a data scheme. Using this scheme, it’s possible to encode base-64 encoded data directly into a URL.

For our purposes, data URLs will take the following form (the spec is a little broader, but we’ll be ignoring the other possibilities):

data:MIME/TYPE;base64,BASE_64_ENCODED_DATA

With MIME/TYPE being replaced with the appropriate MIME-Type for the data, and BASE_64_ENCODED_DATA being replaced with the actual data.

You can get the base64 encoded version of any file using the uuencode terminal command (Linux & Mac). The format of the command is as follows:

uuencode -m IN_FILE REMOTE_NAME

For data URLs, the remote name is irrelevant, but the command insists you pass one, so you can use anything at all for that argument. For example, if I had a file called tag-hash.png which contained an icon I wanted to use to represent a character count, I could determine its base64 encoding with the following command:

uuencode -m tag-hash.png boogers

This produces the following output:

For our purposes, we should ignore both the first and last lines. The data we want is between the header line and the final ==== line with the newline characters removed.

We could update our example to use this icon using the following snippet:

Again, we can put this all together into a full web page as follows (pbs23c.html in the ZIP file):

The big advantage to using data URLs within JavaScript code is that you can share the code with others without having to send them multiple files.

Introducing URI.js

The final piece we need to build our link enhancer is the ability to parse URLs into their component parts so we can analyse them. We could do this with regular expressions, but that’s not as straight forward as you might imagine – the specification defining valid URLs is actually quite complex.

When you encounter a common problem like this, re-inventing the wheel is not a good approach. Instead, you should look to see if there’s an open source project that has already solved your problem for you. I don’t mean copying and pasting snippets of code from a web forum, I mean using a well packaged and well documented library of code that has been specifically designed to be re-used.

When code has been properly designed to be re-used, it will have a well defined and well documented list of available variables, functions, and/or prototypes. Collectively, this documented collection of functions etc. is known as an Application Programming Interface, or API. The API should be all you need to know to make use of a well packaged code library. We’ve already used one such code library in this series – jQuery.

OK, so we need to process URLs, we can’t possibly be the only JavaScript programmers to need that functionality, so there’s probably a well regarded library that solves this problem out there somewhere. A little Googling would tell you that the best regarded JavaScript library for interacting with URLs is URI.js.

This will not be an exhaustive look at URI.js. The documentation on their site is very good, so I’ll leave learning more as an exercise for the reader. Instead, I just want to highlight the parts of the API we’ll need to achieve our goals. Also note that URI.js integrates with jQuery.

Firstly, we can create a URI object representing the URL of the current page as follows:

We can create a URI object from a jQuery object representing a link named $a as follows:

We can determine if a link is relative or absolute using the .is() function:

Finally, we can extract the full domain part of a URL using the .hostname() function:

You can read more about each of these functions, and learn about all the other functions that exist in URI.js’s documentation.

As a worked example, let’s create some code to add the class external to all links in a page that lead to a different site.

External links are those that are not internal. A link is considered internal if either of the following are true; the link is relative, or, the domain the link points to is the same as the domain of the current page.

Based on that, we could write a function to mark external links as external like so:

You can see this function in action in the full web page shown below (pbs23d.html in the ZIP file). Note that it includes CSS definitions to show links in green by default, and links with the class external in red. Also note that this page imports the URI.js library. Because the URI.js project does not provide a CDN, the code library file is included in the zip file in a folder named contrib. This folder must be present in the same folder as pbs23d.html for the library to be successfully imported.

Challenge

Using a slightly altered version of the fourth example (pbs23-assignment.html in the ZIP file) as your starting point, add the code needed to transform external links in the following ways:

  1. Set their target to _blank
  2. Set their rel attribute to noopener
  3. Add an icon after the link (the data URL for the icon is available in the global variable newWindowIconURL)

Conclusions

Hopefully, when you complete the assignment, you’ll have reached an important milestone in this series – our first truly practical piece of code that solves a common real-world problem. To get here we needed an understanding of HTML, CSS, the JavaScript language, and two third-party libraries – jQuery and URI.js.

In the next instalment we’ll tackle another real-world problem, we’ll create an embeddable clock that shows the time in a given timezone. This is something Allison needs for her website. She records her podcast live on Sundays at 5pm her time. To avoid timezone confusions, she would like visitors to her site to see a live clock showing the current time in her timezone.

We already know all the pieces we need to create the clock, the new skill we’ll be learning is how to package our code so as to make it easy to embed into an existing site, and hence, easily re-usable by others. In other words, we’ll be making our own library with its own API for others to re-use on their own pages.