JSRights
In this short project we’ll work with the United States Bill of Rights. Starting with some simple markup and the body content, we’ll use jQuery to generate a table of contents and some simple user interface features.
1. The Plain Old Thing
Let’s start with this very basic HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
|
Also, you’ll want to make a file called styles.css
in the same folder containing these styles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
What you should notice about this document is that each Article starts with an h2
heading then the text of the article is in a paragraph following it. Looking at the document’s head
section you’ll see that it include a CSS stylesheet but nothing else. Open this page in your browser to get a sense of the text.
2. Loading jQuery and Your Javascript
With that baseline setup we can start integrating our Javascript. First, include the jQuery library from Google:
1
|
|
Then we want to include our own Javascript file which we’ll create in a second:
1
|
|
Next create a second file in your project directory named application.js
and open it.
Refresh your browser and there will be no change. We’re loading these Javascript files but we’re not actually doing anything. Now in your application.js
add the following:
1
|
|
Refresh your browser and you should see an alert box indicating that the application.js
file was loaded. Check the RESOURCES tab in Chrome and make sure jQuery is downloading properly. Then you’re good to go for some real development!
3. Back to Top
The first feature we’ll add is a "Back to Top" link under each article. Why do this in Javascript when we could just change the HTML? For one thing, we can keep our text more pure by handling it in Javascript. More importantly, our Javascript approach is flexible and repurposable. If we were dealing with thousands of documents it would work great, where modifying the HTML might take weeks.
We want to insert new content. The process goes something like this:
- Find the places you want the content
- Create the content
- Stick it in that spot
So where do we want the link? It’d be tempting to put the link after every paragraph since each article is just one, but that seems a little sloppy. If there were introductory paragraphs or something they might get links too. Each article is wrapped in a DIV tag with the class name article
so let’s find those DIVs and stick the link just inside the closing DIV tag.
We need a jQuery selector. If you’re familiar with CSS then writing selector statements is super easy. In this case we want DIV tags with the class name article
so we just write "div.article"
. We’ll also need to wrap our jQuery in the document ready lines, so start with this in your application.js
:
1 2 3 |
|
Refresh your browser and you’ll see…no change. We might have selected all the article DIVs, but we didn’t do anything with them. When I’m not sure about a selector I like to first add some CSS attributes to the matched objects. That gives me a visual confirmation that jQuery is finding the things I want it to find. Modify your middle line in application.js
so it’s like this:
1
|
|
Refresh your browser and pink borders should pop around each article. The CSS method sets a CSS property on each element that the selector matched. One thing I love about Chrome is how you can now do "Inspect Element" on those article div
s and it’ll show you, right in the souce code, the CSS that has been dynamically applied. We know our selector is working!
We found the places we want the content, now to create the content. I don’t like to have a whole lot of HTML in my Javascript. In this case we’ll need it, but we can at least abstract it into a variable. Add this line just above your selector line:
1
|
|
We know where the content goes, we’ve got the content, now let’s put it in there. There are several methods for inserting content in jQuery that each have a slightly different purpose. We want to stick content "inside but at the end of" the matched elements. This is an append operation which we can accomplish like this:
1
|
|
Replace the .css
line with that one, refresh your browser, and your Back to Top links should appear. You’ve dynamically created content within the HTML page!
On Your Own: The #top Anchor
These links are great, and when you click on the link from the last article, it will most certainly send you to the top. The problem however, is that it is only the ‘#’ notation that is making this work in the browser currently. <a href='#'>
by itself will get you the same behavior in this example as <a href='#top'>
. But <a href='#'>
is not very explicit, and the default on click with <a href='#'>
is to take you to the top of the current page. So what happens when you a) want to be explicit, as all good developers should, and b) want to target a different article/link? You need an anchor. Insert a named anchor like <a id='top'>
within that h1
at the top of the page and check that your links still work. Now experiment with moving your anchor around in other tags and checking the results.
4. Table of Contents
Jumping back to the top is great and all, but what most users are going to want is to jump from the top to the article they’re interested in. We need to build a table of contents on the fly. How’s that possible?
- Create a ToC heading and unordered list near the top of the page
- Find all the
h2
s, they are the names of the articles - Insert the article names as list items into the UL
- Insert
id
anchors into each of theh2
s - Link the Table of Contents item to the
h2
Create a ToC Heading and Unordered List
We’ve already practiced creating a variable to hold HTML then inserting that HTML. Store "<h2 id='toc_header'>Table of Contents</h2>"
into a variable and insert it into the markup just below the existing H1. Instead of using append
like we did before, here you need after
so it goes outside the closing H1 tag.
Then, just under that heading, insert an unordered list (UL) with the id attribute set to 'toc_list'
. The tricky thing here is that you probably want to use a selector to find the ToC h2
, but how can we do that without getting all the h2
s? Use the CSS-style virtual attribute :first
like h2:first
.
About id
s
The id attribute provides a unique identifier for an element within the document.
The most important aspect of the id attribute is that it must be absolutely unique. Unlike the class attribute, which may apply the same value to many elements in a page, an id that’s applied to an element must not match an id used anywhere else on the same page.
Note that the id attribute cannot be applied to the following elements:
base
head
html
meta
script
style
title
Refresh the page in your browser, use the Inspect Element tool, and make sure your new HTML is getting injected properly.
Finding the Titles and Inserting them into the ToC
First, on your own, write a selector which finds all the article h2
s and sets their background color to #CCF. Make sure it’s not selecting the heading ToC heading.
Now that you’ve found the h2
s, we need to grab their content and create the list items. Our selector is finding the collection of h2
s, then we can use the each
method to run instructions on each element of the collection. Here’s how it’ll look:
1 2 3 |
|
We pass a function
into each
holding the instructions we want run on each element. Remember that we bundle it into a function so the instructions don’t get executed when the script is first read, they wait until the function is called.
We want to pull the text out of the object. How do we get to the one object we’re looking at within the collection? We can try using the special variable this
like so:
1 2 3 4 |
|
Refresh your browser and you won’t see any alert boxes popping up. Look at your developer pane and you’ll see an error like Object #<an HTMLHeadingElement> has no method 'text'
. You get this error because within the each
method, the this
variable refers to the plain DOM object. It isn’t a jQuery object, so we can’t call a jQuery method like .text
or else we see this error.
But it allows us to show another usage style for the $
jQuery reference. Modify your code block to pass this
into $
like so:
1 2 3 4 |
|
Now refresh your page and you should see a bunch of popups with the individual article titles. It works! Now, what were we trying to do again? We wanted to insert these titles into the ToC list. Instead of alerting the title, let’s build it into an li
like this:
1
|
|
Then, on your own, write a selector to find the ToC UL and append
the listItem
. Refresh your browser and you should see a plain text Table of Contents.
Linking
Our ToC is nice for reading and printing, but it should be linked. We want to click on an article title in the ToC and jump to the article further down the page. To accomplish this interaction we need to:
- Take the title and convert it to a "slug" usable in a URL
- Insert anchors in the individual article
h2
s which have theid
set to that slug - Link the list items in the ToC to that slug
Creating a Slug
We already have the title
variable which holds name of the article like "Article X". We need to create a slug version of that, conventionally all lowercase and spaces replaced by underscores like article_x
. We’ll use a mix of jQuery methods and normal Javascript methods to accomplish this translation.
It’s a great application of method chaining, but we need to be aware of what object type we’re expecting back. The jQuery methods should give jQuery objects back, while the Javascript methods are going to give Javascript objects. A jQuery object knows the Javascript methods too, but a Javascript object does not know the jQuery methods – so our ordering will be important. Let’s start with these lines just under your current title =
line.
1 2 |
|
Refresh your browser and you’ll just see the normal titles in alert boxes. As a first step I want to use the jQuery method .trim()
which cuts any whitespace off the frontend or backend of our string. Now those lines will look like this:
1 2 |
|
Remember that in Javascript we have to put the method’s parentheses even if there are no parameters. Now we’ll use some normal Javascript methods to manipulate the string further.
Right after your trim()
call, add on a call to .toLowerCase()
to convert it to lower case. Then we need the .replace()
method which takes two parameters: the string to find and the string to replace it with. Use it to replace a space with an underscore. Combining them all gives me this line:
1
|
|
Refresh your browser to check that the slugs look good and we can move on.
Insert the Target Anchors
Now that we have the slug we can setup the target anchors. Within the each
block that we’ve been working in, remember that this
is referring to the h2
. We want to inject the anchor inside that h2
. Let’s create the anchor and insert it in one step. We’ll use a handy jQuery method called attr
. We’ll call attr
on the jQuery version of this
, which remember represents each of our h2
s. And then we’ll pass two arguments to attr
, the first of which declares the attribute we want to add: id
, the second argument is the value of that attribute, which in our case is the previously defined variable slug.
1
|
|
Link to the Targets
We need to add links into the listItem
. On your own, work with the var list_item=
line to include a link tag where the href
points to #article_x
where article_x
is the current slug.
Remove any alert
lines you have and refresh your browser. Your ToC should be fully functional!
5. Hiding Content
There’s one more feature I want in our document. Underneath the title of each article I want a (hide)
link which, when clicked, hides the body of the article and changes the link to say (show)
. Here’s what we need to do:
- Create the hide link
- Attach a
click
event handler that…- Hides the article content
- Changes the link text
Creating the link itself is the easy part. You already have the selector for all article h2
s and the each
block of instructions, so we’ll continue to work within that each
block. Add these two lines to create a simple link and insert it after the h2
:
1 2 3 |
|
You’ll notice that toggleLink
is a jQuery object, not just a normal Javascript string, because I put it inside $()
. We’ll need that jQuery functionality soon. The second line inserts the link object after the h2
. Refresh your browser and you’ll see the links appear, but when you click them nothing meaningful happens.
Working with the Click Event
We need to add behavior that will be executed when that link is clicked. In between the two lines you just wrote, add this:
1 2 3 |
|
What is that? The on
method binds the given event to the given function. In this case, the event we’re listening for is click
, which is fired when there is a mouse click on that element. on
takes a function
parameter which holds the code that will be executed when the click happens. Here our function takes a parameter event
which effectively creates a local variable named event
that contains a bunch of information about when and where the click happened. So far we’re not using that variable for anything. Then inside the function we get the link with this
, turn it into a jQuery object with $()
, and call the siblings
method. This method returns the set of all "sibling" objects in the DOM, the objects that share the same parent/wrapper object as this one. In our case that will be all DOM objects inside the article DIV. Once those siblings are found it calls hide
on them to hide them from view.
Now that the click
listener is attached to toggleLink
we need to insert the link into the DOM. Use this line outside of the click
block:
1
|
|
Try it in your browser. It kinda works, right? What’s the main issue?
We can fix that by filtering the siblings
matcher. Change siblings()
to siblings('p')
so it’ll only match siblings which are paragraphs and retry it. Now your article titles won’t disappear.
Revealing the Text
Hiding is cool and all, but how about revealing? If we click the link again it should reveal the hidden text. In jQuery there’s the hide
method we just used and there’s show
which works exactly the same. We could use an if/else block to say "when this link is clicked, if the text is visible then hide it, otherwise reveal it." That’d work, but it’s a common enough use case that jQuery makes it easier on us.
Several methods come in pairs like hide
and show
. Many such pairs have a third instruction that toggles between them, looking at the element and figuring out which one should be done. In this case, the toggle method is called toggle
. All we need to do is change our method call from hide()
to toggle()
.
Try that out in your browser.
Changing the Link Text
It works great but it looks stupid. When the text is hidden the link still says "hide" – it should switch to saying "show." It’d be nice if there were some toggle-like function to change text and there is discussion about it existing in the future. For now, though, we have to toggle the link text manually.
We’ll do it in two steps:
- Figure out what the text should be
- Set the link to that text
Here’s the logic to figure out the text: "If the link currently says '(hide)'
then the text should be '(show)'
, otherwise it should be '(hide)'
. We could use a Javascript if/else construction to get this accomplished, but when there is just one step in the "then" clause we can use the ternary operator.
Present in several languages, the ternary operator is one of those things that looks crazy but is really handy. Here’s how it’s constructed:
1
|
|
The condition is evaluated to true or false. If it’s true then the what_to_do_if_true
instruction will be executed and the result stored into result
. If the condition was not true the what_to_do_if_false
is executed and it’s result put into result
.
In our case, here’s how we’ll use it:
1 2 |
|
Setting the Link Text
Now newText
will hold the value of what we want the link to say after this click. Figure out how to use the text
method to set the text of the link to new_text
.
Try it out in your browser and it’s functionally good, but there’s a usability issue.
Stop Jumping Around, Browser!
When you click show or hide links the browser is jumping to the top of the page, right? That’s because in addition to executing our Javascript listening for the click
event, it’s also doing the normal things it does when you click a link – trying to load the page or anchor. Our actual link tag points to #
, a blank anchor, which is the conventional pointer for "do some Javascript but don’t actually load any HTML." But the browser is looking for an anchor tag with a blank name and, not finding it, jumps you to the beginning of the page. Annoying.
The fix is simple. We want the browser to not react to the link click itself, just run our Javascript. jQuery has a method for doing this which encapsulates the differences between browsers. It’s acutally a method of the event object itself which is why we put event
into the click
method signature. All you need to do is add this as the first line inside your click
block:
1
|
|
Refresh your browser and try it out; there should be no more jumping around. Our hide/show functionality is complete!
6. Refactoring to a plugin
Plugins make jQuery an excellent language for sharing re-usable pieces of code. As we saw in this example, we created a script that goes through a page featuring div.article
sections with h2
tags and built up a table of contents to place under the h1
. We also using lots of jQuery functions that operated on selected tags, for example, $('div.article').each(...
. We’re going to create our own method that can operate on tags using jQuery’s plugin architecture.
Here’s how we’re going to setup our plugin and call it when the document loads:
1 2 3 4 5 6 7 |
|
By adding a function to $.fn
we can access it when selecting tags. In our case we select all of our div.article
tags and call our function. We also pass in the header to the plugin function so it will know where to put our ToC.
Only a few changes need to be made to our code in order to use it inside the plugin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Now we are free to distribute this plugin among our team or publicly for the world to use to build tables of contents for their article lists.
Wrapup
In this short project we took relatively plain HTML text and augmented it with navigation, a table of contents, and show/hide functionality. While much of this would be possible by editing the HTML itself, our Javascript technique keeps the content more "pure" and brings the "function" all into one place. This approach would also scale to multiple pages, where editing the HTML would be time consuming.
In practicing our jQuery and pure Javascript we…
- Created and worked with variables
- Used text manipulation methods like
toLowerCase
andtext
- Used the ternary operator
- Responded to a click event
- Inserted, hid, and displayed content
- Used a variety of selectors with IDs, Classes, and Virtual Attributes
- Refactored our script into a reusable plugin