O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

12.11. Replacing Links with Footnote Bullets

Problem

You want to scan a web page for links, remove the links from the page, and replace them with text-based footnote bullets at the end of the document.

Solution

You’ll have to use a variety of techniques to accomplish this task. Example 12-3 demonstrates a full application that moves all of the links contained in a paragraph to a bulleted footnote list at the end of the document—copying the contents of the link to the original link position first, and adding a superscript to reference the new footnote.

Example 12-3. Application to pull links out of the web document and append in a list at the end

<!DOCTYPE html>
<head>
<title>Moving Links</title>
<style>
 ul li
  {
    list-style-type: none;
    padding-bottom: 5px;
  }
</style>
<script type="text/javascript">

window.onload=function() {

  var links = document.querySelectorAll("a");
  var footnote = document.createElement("ul");

  // for all links
  for (var i = 0; i < links.length; i++) {

    // get parent element
    var parent = links[i].parentNode;

    // create number index text
    var num = document.createTextNode(i+1);
    var sup = document.createElement("sup");
    sup.appendChild(num);

    // process the children
    var children = links[i].childNodes;
    for (var j = 0; j < children.length; j++) {
         var newChild = children[j].cloneNode(true);
         parent.insertBefore(newChild,links[i]);
    }

    // add number subscript
    var sup2 = sup.cloneNode(true);
    parent.insertBefore(sup2,links[i]);

    // add a link to footnote
    var li = document.createElement("li");
    li.appendChild(sup);
    li.appendChild(links[i]);

    footnote.appendChild(li);
  }

  document.getElementsByTagName("body")[0].appendChild(footnote);}
</script>
</head><body>  <div id="target">
    <p>A favorite place of mine to visit in St. Louis is the <a
href="http://http://www.mobot.org/">Missouri Botanical Gardens</a>.
Great flowers all year round, and one of the finest annual
orchid shows. My most visited places, though, are the <a
href="http://www.stlzoo.org/">St.
Louis Zoo</a>, the <a href="http://www.nps.gov/jeff/index.htm"><em>Gateway
Arch</em></a>,
 the new <a href="http://www.citygardenstl.org/">City Garden</a>,
and the <a href="http://mdc.mo.gov/areas/cnc/powder/">Powder
Valley Conservation Nature Center</a>.
    </p>
  </div>
</body>

Discussion

As demonstrated in the solution, you can use querySelectorAll to find all the links in the page, passing in the anchor tag:

var links = document.querySelectorAll("a");

You can’t use getElementsByTagName to get the links, and I’ll explain why a little later in the discussion. The solution also creates a new unordered list to contain the links we pull out of the document.

Once you have a reference to all the links in the page, it’s time for the fun part. One of the interesting challenges with moving a link is that you typically want to preserve the link’s text in place—especially if the link references meaningful content—beyond the to-be-discouraged “here” or “link”.

You can’t assume the link contents are text because a link can contain other elements (though not another link). You could use innerHTML to access and copy the contents, but innerHTML isn’t well supported in XHTML.

Another approach is to move all of the child nodes out of the link. Now, you might think a way to move the link contents out of the link is to use something like the following:

var children = links[i].childNodes;
for (var j = 0; j < children.length; j++) {
     parent.insertBefore(children[j],links[i]);
}

The problem with this approach, though, is that childNodes points to a nodeList. In Chapter 11, we learned that nodeLists are live collections, which means changes in the page are reflected immediately in the nodeList in our code. So, if the link is something like the following:

<a href="http://oreilly.com">O'Reilly sells <em>books!</em></a>

The childNodes collection would have a length of 2 for the text node and the em. However, in the first loop iteration, once the first element has been moved (in this case, inserted before the existing link’s location, but it could be moved to the bullet list), the length of the childNodes value is now 1, but the for loop iterator has been incremented to 1—there is now only one child in the collection, and the for loop exits, leaving us with a link with the contents of the em element.

This is all really nice functionality, but it works against us sometimes. This is also the reason I didn’t use the getElementsByTagName method to get a list of the anchor tags. The getElementsByTagName also returns a live nodeList, and when we append the links to the in-memory unordered list, the link is removed from the document, and the length of the collection is affected.

Luckily, as the solution demonstrates, we have other methods that do what we want, without the interesting side effects. The cloneNode method is used to clone the child elements, which the solution places just before the link. Once the children are taken care of, the entire link element is moved to the bulleted list using appendChild on a newly created list element (li), which is then appended to our unordered list (ul).

When using cloneNode, especially in a circumstance such as this, you’ll want to pass a parameter of true when cloning the node:

var newElement = oldElement.cloneNode(true);

This ensures that all of the node’s children are also cloned in place, in addition to the element. Once all of the links are processed in the document, the last act is to append the unordered list to the end of the document. Figure 12-3 shows the web page after the JavaScript is finished.

The application works with all the book’s target browsers. It doesn’t work with IE7, because that browser version does not support the querySelectorAll method.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required