Click here to Skip to main content
15,879,348 members
Articles / Programming Languages / Javascript

Breadcrumbs Generation Tool

Rate me:
Please Sign up or sign in to vote.
4.29/5 (7 votes)
29 Jan 2020CPOL9 min read 8.9K   70   7   3
This article presents a method that provides developers with the ability to generate traversal history breadcrumbs dynamically.

1. Introduction Table of Contents

Depending upon which form is desired, a breadcrumb trail

  • Tracks and displays each page viewed by a visitor of a website in the order in which the pages were viewed
  • Displays the hierarchy of the current page in relation to the website's structure

This article will address only the first form.

For each breadcrumb in a breadcrumb trail, the breadcrumb is made up of two pieces: a label and a path. In the breadcrumb string that appears on the webpage, there is a separator between breadcrumbs (not present following the current page label). This separator is usually the greater than symbol (>). The path of the last breadcrumb is suppressed.

This article will present a method whereby breadcrumbs can be generated dynamically.

1.1. Development Directory Structure Table of Contents

As I coded the JavaScript for this project, I gradually came to the conclusion that there needed to be two separate modules: one for a stand-alone situations and the other for master-page situations. As a result, I created two directory structures (both included in the download) for testing the breadcrumb generation tool:

StandAloneBreadcrumbs       MasterPageBreadcrumbs
  CSS                         CSS
    breadcrumbs.css             master_page.css
  Scripts                     Images
    breadcrumbs.js              favicon.ico
  index.html                    green_dot.png
  link_1.html                   ocean2.gif
  link_2.html                   printer.png
  link_3.html                   printer_hover.png
  link_4.html                   site_logo.png
                                under_construction.png
                              Scripts
                                master_page.js
                              contact_webmaster.html
                              index.html
                              link_1.html
                              link_2.html
                              link_3.html
                              link_4.html
                              privacy_policy.html

Using the MasterPageBreadcrumbs directory, say the reader has taken the following path to the link_4.html page:

index.html → link_1.html → link_3.html → link_4.html

and the developer has chosen "Home", "Journeys", "Programming", and "Ethics" as the labels for the breadcrumb components, then the breadcrumb string to be displayed on the link_4.html page would be:

Home  >  Journeys  >  Programming  >  Ethics

Because there is no traversal history inherent in the paths to each page, as each page is visited, it is necessary to record the visit as well as the traversal order. The traversal history string, defined for this project that records the traversal to the ethics.html page is:

Home>index.html|Journeys>link_1.html|Programming>link_3.html

where > separates the label from its path and | separates label-path pairs.

1.2. Saving the Traversal History String Table of Contents

The traversal history string is ephemeral; it changes as each page is traversed, recording the history up through the last page visited. But in addition to recording the traversal of webpages, the traversal history string must survive browser refreshes and tab manipulation but cease to exist at the end of the browser session.

There are a number of ways that a traversal history string can be saved:

  • document.cookie
  • window.localStorage
  • window.sessionStorage

The requirements levied against the traversal history string are only met by the document.cookie property, specifically a session cookie.

1.3. Blocked Cookies Table of Contents

If a visitor does not allow cookies, then the JavaScript must fail unobtrusively by not providing breadcrumbs.

1.4. European Union "Cookie Policy" Table of Contents

The European Union Cookie Policy [ ^ ] forbids the use of cookies without informed consent in advance of their use. However, this restriction does not apply to cookies that are used for website technical purposes. The cookies used in breadcrumbs are therefore excluded from the EU restriction.

2. Using the Code Table of Contents

Taking a lesson from Master Pages using HTML, CSS, and JavaScript [ ^ ], I decided that the generation of breadcrumbs should be directed by the contents of a JSON parameter declared in the <head> of each page (that is to display breadcrumbs) and then passed to build_breadcrumbs, the event handler for the <body> element's onload event.

In this article,

  • The term "current page" refers to the HTML page that is about to be displayed.
  • The symbols < and > (note bolded) surround user supplied information.

2.1. Master Page Breadcrumbs Table of Contents

The following are the prerequisites for generating master-page breadcrumbs:

  • master_page.js must be included in the current page, as in:
    C#
    <script src="<scripts-directory>/master_page.js"></script>
    
  • The build_page method must be invoked as part of the form load event, as in:
    C#
    <body onload="MasterPage.build_page ( PAGE_COMPONENTS );">
    

2.2. Stand-alone Breadcrumbs Table of Contents

The following are the prerequisites for generating stand-alone breadcrumbs:

  • breadcrumbs.js must be included in the current page, as in:
    C#
    <script src="<scripts-directory>/breadcrumbs.js"></script>
    
  • The build_breadcrumbs method must be invoked as part of the form load event, as in:
    C#
    <body onload="BreadCrumbs.build_breadcrumbs ( PAGE_COMPONENTS );">
    

2.3. PAGE_COMPONENTS Table of Contents

If default breadcrumb processing is desired, no PAGE_COMPONENTS parameter needs to be passed to either the build_page or the build_breadcrumbs methods. In this case, the breadcrumb label for the page will be the filename of the page's URL (document.URL) and the label's casing will be title case.

If special processing is desired, there are four recognized name/value pairs in the JSON object that control the processing, for both stand-alone and master-page breadcrumb generation:

  • breadcrumbs_desired - recognized values are true and false
  • breadcrumb_label_source - recognized values are:
    • given - the label is given in the breadcrumb_label
    • filename - the label is extracted from the filename of the page URL
    • title - the label is obtained from the text in the page <title>
  • breadcrumb_label - a string containing the desired page label; if omitted, the filename of the page URL will be used
  • breadcrumb_label_casing - recognized values are:
    • lower - the breadcrumb label will be displayed in lowercase
    • title - the breadcrumb label will be displayed in title case
    • upper - the breadcrumb label will be displayed in uppercase

For example:

C#
<head>

  <title>Home</title>

  <meta http-equiv="Content-type"
        content="text/html; charset=UTF-8" />
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0" />

  <script type="text/javascript">
    var PAGE_COMPONENTS =
      {

      "breadcrumbs_desired":true,
      "breadcrumb_label_source":"given",
      "breadcrumb_label":"home",
      "breadcrumb_label_casing":"title",

      };
  </script>

</head>

For master-pages the PAGE_COMPONENTS parameter just has these four name/value pairs added; for stand-alone pages the PAGE_COMPONENTS parameter just has these four name/value pairs (with possibly debug_json present).

2.4. Top-most Website Page Table of Contents

It is probable that the website's top-most page will be accessed from outside the website (e.g., from Google, etc.). When that occurs, an existing breadcrumbs cookie may exist. To keep that cookie from interfering with breadcrumb generation, the cookie should be removed.

  • In the case of master-page websites:
    C#
    <script type="text/javascript">
        if ( MasterPage.cookie_exists (
                                MasterPage.BREADCRUMB_COOKIE_NAME ) )
          {
          MasterPage.erase_cookie (
                                MasterPage.BREADCRUMB_COOKIE_NAME );
          }
    </script>
    
  • In the case of stand-alone websites:
    C#
    <script type="text/javascript">
        if ( BreadCrumbs.cookie_exists (
                                BreadCrumbs.BREADCRUMB_COOKIE_NAME ) )
          {
          BreadCrumbs.erase_cookie (
                                BreadCrumbs.BREADCRUMB_COOKIE_NAME );
          }
    </script>
    

3. Implementation Table of Contents

The implementation of the breadcrumb generator is contained in the bread_crumbs.js and the master_page.js JavaScript files. The logic is somewhat the same, but because master_page.js is a little more complicated, we will address it here.

  1. If the current page is not a descendent of the referrer, then the breadcrumbs cookie must be eliminated. The implication is that we got to the current page from some web location outside the web site (say from Google), so we must erase the breadcrumb cookie.
    C#
    :
    var document_path = "";     // path of current page
    var referrer_path = "";     // path of referrer
    :
    document_path = retrieved_path ( document.URL );
    referrer_path = retrieved_path ( document.referrer );
    
    if ( document_path !== referrer_path )
      {
      if ( cookie_exists ( BREADCRUMB_COOKIE_NAME ) )
        {
        erase_cookie ( BREADCRUMB_COOKIE_NAME );
        }
      }
    
    retrieved_path returns a URL stripped of the filename and extension.
  2. The current page must support certain methods and properties; if not, fail unobtrusively (return without generating breadcrumbs).
    C#
    :
    if ( !document.createElement ||
         !document.getElementById ||
         !document.title ||
         !document.URL ||
         !window.navigator.cookieEnabled )
      {
      return;                   // fail unobtrusively
      }
    
  3. If the document contains a breadcrumbs_div <div> element, make sure it is a <div>; otherwise set it to null
    C#
    :
    var breadcrumbs_div;        // target div in the document
    :
    breadcrumbs_div = document.getElementById (
                                              BREADCRUMB_DIV_NAME );
    if ( breadcrumbs_div )
      {
      if ( typeof breadcrumbs_div !== "div" )
        {
        breadcrumbs_div = null;
        }
      }
    
  4. There are slight differences between master-page and stand-alone versions:
    • For master-pages breadcrumbs, if the document contains a breadcrumbs_div element, use it; otherwise if breadcrumbs are desired, create a breadcrumbs_div element following the header and if there is no <header> div create a breadcrumbs_div element at the beginning of the document body.
      C#
      if ( breadcrumbs_div )
        {
                                        // no action needed - use it
        }
      else if ( components.breadcrumbs_desired )
        {
        var header_div = document.getElementById ( 'header' );
      
        breadcrumbs_div = document.createElement ( "div" );
        breadcrumbs_div.id = BREADCRUMB_DIV_NAME;
      
        if ( header_div )
          {
          header_div.appendChild ( breadcrumbs_div );
          }
        else
          {
          document.body.insertBefore ( breadcrumbs_div,
                                       document.body.
                                           firstElementChild.
                                           nextSibling);
          }
        }
      else
        {
        return;                         // fail unobtrusively
        }
      
    • For stand-alone breadcrumbs, if the document contains a breadcrumbs_div element, use it; otherwise create a breadcrumbs_div element at the beginning of the document body. Note that breadcrumbs_desired is implied by build_breadcrumbs having been invoked.
      C#
      if ( breadcrumbs_div )
        {
                                        // no action needed - use it
        }
      else
        {
        breadcrumbs_div = document.createElement ( "div" );
        breadcrumbs_div.id = BREADCRUMB_DIV_NAME;
        document.body.insertBefore ( breadcrumbs_div,
                                     document.body.
                                         firstElementChild.
                                         nextSibling);
        }
      
  5. Create the <div> that will replace breadcrumbs_div in the document,
    C#
    :
    var breadcrumbs;            // breadcrumbs to appear
    :
    breadcrumbs = document.createElement ( "div" );
    breadcrumbs.id = BREADCRUMB_DIV_NAME;
    
  6. Determine from whence cookies are to be retrieved: from the filename in document.URL? from the text in the document.title? from a user given value in breadcrumb_label in preferences? - defaults to the filename in the document.URL; retrieve and trim the label.
    C#
    :
    var breadcrumb_label = "";  // this page's breadcrumb label
    :
    breadcrumb_label = extract_filename ( document.URL );
    if ( preferences.breadcrumb_label_source )
      {
                                // defaults to filename in the
                                // document.URL
      switch ( preferences.breadcrumb_label_source.toLowerCase ( ) )
        {
        case "title":
          breadcrumb_label = document.title;
          break;
    
        case "given":
          if ( preferences.breadcrumb_label )
            {
            breadcrumb_label = preferences.breadcrumb_label;
            }
          break;
    
        case "filename":
        default:
    
          break;
        }
      }
    breadcrumb_label = trim ( breadcrumb_label );
    
  7. Set the label's casing; defaults to title case.
    C#
    :
    if ( preferences.breadcrumb_label_casing )
      {
      switch ( preferences.breadcrumb_label_casing.toLowerCase ( ) )
        {
        case "lower":
          breadcrumb_label = breadcrumb_label.toLowerCase ( );
          break;
    
        case "upper":
          breadcrumb_label = breadcrumb_label.toUpperCase ( );
          break;
    
        case "title":
        default:
          breadcrumb_label = to_titlecase ( breadcrumb_label );
          break;
        }
      }
    
  8. Generate the current page's breadcrumb.
    C#
    :
    var LABEL_URL_SEPARATOR = ">";
      :
      var page_label_URL = "";    // current page's label/URL
    
      :
      page_label_URL = breadcrumb_label +
                       LABEL_URL_SEPARATOR +
                       document.URL;
    
  9. Retrieve the existing breadcrumb cookie string.
    C#
    :
    var cookie_string = "";     // existing breadcrumb cookies
    :
    cookie_string = read_cookie ( "breadcrumb" );
    
  10. Revise cookie_string as required.
    C#
    :
    var LABEL_URL_PAIR_SEPARATOR = "|";
      :
      var index = -1;             // general purpose index
      :
      if ( IsNullOrEmpty ( cookie_string ) )
        {
        cookie_string = page_label_URL;
        }
      else
        {
                                  // determine if the current
                                  // page URL is already in the
                                  // cookie string; required if
                                  // visitor backs up through
                                  // the breadcrumbs
        index = cookie_string.indexOf ( document.URL );
        if ( index >= 0 )
          {
                                  // URL is already in the
                                  // cookie string; make it the
                                  // last entry
          index = cookie_string.indexOf ( LABEL_URL_PAIR_SEPARATOR,
                                          index );
                                  // index < 0 implies URL is at
                                  // the end of the cookie
                                  // string; no action necessary
          if ( index < 0 )
            {
    
            }
          else
            {
                                  // index >= 0 implies cookie
                                  // string must be truncated
                                  // at the pair separator
                                  // following the current page
                                  // URL
            cookie_string = cookie_string.slice ( 0, index );
            }
          }
        else
          {
          cookie_string += LABEL_URL_PAIR_SEPARATOR + page_label_URL;
          }
        }
    
  11. From the cookie string build the current page breadcrumbs.
    C#
    :
    var label_URLs;             // array of labels and URLs
    :
    label_URLs = cookie_string.split ( LABEL_URL_PAIR_SEPARATOR );
    for ( var i = 0; ( i < label_URLs.length ); i++ )
      {
      var label_URL;
      var span;
    
      label_URL = label_URLs [ i ].split ( LABEL_URL_SEPARATOR );
      if ( IsNullOrEmpty ( label_URL [ 0 ] ) ||
           IsNullOrEmpty ( label_URL [ 1 ] ) )
        {
        continue;
        }
    
      span = document.createElement ( "span" );
    
      if ( i < ( label_URLs.length - 1 ) )
        {
        var a = document.createElement ( "a" );
    
        a.href = label_URL [ 1 ];
        a.innerText = label_URL [ 0 ];
        bread_crumbs.appendChild ( a );
    
        span.innerHTML = " > ";
        bread_crumbs.appendChild ( span );
        }
      else
        {
        span.innerHTML = label_URL [ 0 ];
        bread_crumbs.appendChild ( span );
        }
      }
    
  12. Replace the current page breadcrumb <div> with the new one.
    C#
    :
    breadcrumbs_div.parentNode.replaceChild ( bread_crumbs,
                                              breadcrumbs_div );
    
  13. Replace the old breadcrumb cookie string with the new one.
    C#
    :
    erase_cookie ( "breadcrumb" );
    create_cookie ( "breadcrumb",
                    cookie_string,
                    0 );              // make a session cookie
    

The helper functions

  • cookie_exists
  • create_cookie
  • erase_cookie
  • extract_filename
  • IsNullOrEmpty
  • read_cookie
  • to_titlecase

are all included in the appropriate JavaScript file.

4. References Table of Contents

5. Downloads Table of Contents

The download contains the two development directories. In the appropriate Scripts/ directory are found master_page.js and breadcrumbs.js.

In both development directories, in the CSS/ directories are found CSS files that contain:

CSS
#breadcrumbs
  {
  padding-left:15px;
  font-size:smaller;
  }

This style causes the breadcrumbs to be displayed indented and in a smaller font.

The master-page.js file is found in the Scripts/ subdirectory of the MasterPageBreadcrumbs/ directory; the breadcrumbs.js file is found in the Scripts/ subdirectory of the StandAloneBreadcrumbs/ directory.

6. Practice and Experience Table of Contents

Stand-alone breadcrumbs was developed first. It was not until late in the development process that a need for a separate master-pages version was recognized.

6.1. Incorporating breadcrumbs.js into master_page.js Table of Contents

The first step was to incorporate breadcrumbs.js into master_page.js. It became apparent that large portions of breadcrumbs.js already existed in master_page.js. As a result, only the following modifications were needed:

  • The build_breadcrumbs invocation was added to build_page.
  • The constants BREADCRUMB_COOKIE_NAME, BREADCRUMB_DIV_NAME, BREADCRUMB_LABEL_URL_PAIR_SEPARATOR, and BREADCRUMB_LABEL_URL_SEPARATOR were added.
  • The functions to_titlecase, trim, IsNullOrEmpty, extract_filename, parse_URL, retrieved_path, and build_breadcrumbs were added.
  • The entry point build_breadcrumbs was added to the public properties of the namespace MasterPage.

6.2. Incorporating Breadcrumbs into an existing Website Table of Contents

I chose to incorporate breadcrumbs into my own website [ ^ ]. Due to the menu structure of the site, I really don't think that breadcrumbs are needed (except for pedagogical reasons to support this article).

The incorporation required the following steps, performed on my local computer.

  • Replace the master-page.js on the website with the master_page.js from the MasterPageBreadcrumbs directory.
  • Add "breadcrumbs_desired", "breadcrumb_label_source", "breadcrumb_label", and "breadcrumb_label_casing" to the PAGE_COMPONENTS on each web page in the website.
  • Add the required <script> at the end of index.html.

With the exception of adding breadcrumb parameters to PAGE_COMPONENTS, the task was not too onerous rather it was repetitive.

7. Conclusion Table of Contents

I have presented a method that provides developers with the ability to generate traversal history breadcrumbs dynamically.

8. Development Environment Table of Contents

The Breadcrumbs Generation Tool was developed in the following environment:

Microsoft Windows 7 Professional SP 1
Microsoft Visual Studio 2008 Professional SP1
Microsoft Visual C# 2008
Microsoft .Net Framework Version 3.5 SP1

9. History Table of Contents

01/27/2020 Original article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
In 1964, I was in the US Coast Guard when I wrote my first program. It was written in RPG (note no suffixing numbers). Programs and data were entered using punched cards. Turnaround was about 3 hours. So much for the "good old days!"

In 1970, when assigned to Washington DC, I started my MS in Mechanical Engineering. I specialized in Transportation. Untold hours in statistical theory and practice were required, forcing me to use the university computer and learn the FORTRAN language, still using punched cards!

In 1973, I was employed by the Norfolk VA Police Department as a crime analyst for the High Intensity Target program. There, I was still using punched cards!

In 1973, I joined Computer Sciences Corporation (CSC). There, for the first time, I was introduced to a terminal with the ability to edit, compile, link, and test my programs on-line. CSC also gave me the opportunity to discuss technical issues with some of the brightest minds I've encountered during my career.

In 1975, I moved to San Diego to head up an IR&D project, BIODAB. I returned to school (UCSD) and took up Software Engineering at the graduate level. After BIODAB, I headed up a team that fixed a stalled project. I then headed up one of the two most satisfying projects of my career, the Automated Flight Operations Center at Ft. Irwin, CA.

I left Anteon Corporation (the successor to CSC on a major contract) and moved to Pensacola, FL. For a small company I built their firewall, given free to the company's customers. An opportunity to build an air traffic controller trainer arose. This was the other most satisfying project of my career.

Today, I consider myself capable.

Comments and Discussions

 
Questioncode download Pin
baldax5630-Jan-20 0:38
baldax5630-Jan-20 0:38 
AnswerRe: code download Pin
gggustafson30-Jan-20 6:20
mvagggustafson30-Jan-20 6:20 
AnswerRe: code download Pin
gggustafson30-Jan-20 6:50
mvagggustafson30-Jan-20 6:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.