Table of Contents
The symbol returns the reader to the top of the Table of Contents.
1. Introduction
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
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
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
If a visitor does not allow cookies, then the JavaScript must fail unobtrusively by not providing breadcrumbs.
1.4. European Union "Cookie Policy"
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
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
The following are the prerequisites for generating master-page breadcrumbs:
2.2. Stand-alone Breadcrumbs
The following are the prerequisites for generating stand-alone breadcrumbs:
2.3. PAGE_COMPONENTS
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:
<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
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:
<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:
<script type="text/javascript">
if ( BreadCrumbs.cookie_exists (
BreadCrumbs.BREADCRUMB_COOKIE_NAME ) )
{
BreadCrumbs.erase_cookie (
BreadCrumbs.BREADCRUMB_COOKIE_NAME );
}
</script>
3. Implementation
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.
- 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.
:
var document_path = "";
var referrer_path = "";
:
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. - The current page must support certain methods and properties; if not, fail unobtrusively (return without generating breadcrumbs).
:
if ( !document.createElement ||
!document.getElementById ||
!document.title ||
!document.URL ||
!window.navigator.cookieEnabled )
{
return;
}
- If the document contains a breadcrumbs_div <div> element, make sure it is a <div>; otherwise set it to null
:
var breadcrumbs_div;
:
breadcrumbs_div = document.getElementById (
BREADCRUMB_DIV_NAME );
if ( breadcrumbs_div )
{
if ( typeof breadcrumbs_div !== "div" )
{
breadcrumbs_div = null;
}
}
- 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.
if ( breadcrumbs_div )
{
}
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;
}
- 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.
if ( breadcrumbs_div )
{
}
else
{
breadcrumbs_div = document.createElement ( "div" );
breadcrumbs_div.id = BREADCRUMB_DIV_NAME;
document.body.insertBefore ( breadcrumbs_div,
document.body.
firstElementChild.
nextSibling);
}
- Create the <div> that will replace breadcrumbs_div in the document,
:
var breadcrumbs;
:
breadcrumbs = document.createElement ( "div" );
breadcrumbs.id = BREADCRUMB_DIV_NAME;
- 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.
:
var breadcrumb_label = "";
:
breadcrumb_label = extract_filename ( document.URL );
if ( preferences.breadcrumb_label_source )
{
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 );
- Set the label's casing; defaults to title case.
:
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;
}
}
- Generate the current page's breadcrumb.
:
var LABEL_URL_SEPARATOR = ">";
:
var page_label_URL = "";
:
page_label_URL = breadcrumb_label +
LABEL_URL_SEPARATOR +
document.URL;
- Retrieve the existing breadcrumb cookie string.
:
var cookie_string = "";
:
cookie_string = read_cookie ( "breadcrumb" );
- Revise cookie_string as required.
:
var LABEL_URL_PAIR_SEPARATOR = "|";
:
var index = -1;
:
if ( IsNullOrEmpty ( cookie_string ) )
{
cookie_string = page_label_URL;
}
else
{
index = cookie_string.indexOf ( document.URL );
if ( index >= 0 )
{
index = cookie_string.indexOf ( LABEL_URL_PAIR_SEPARATOR,
index );
if ( index < 0 )
{
}
else
{
cookie_string = cookie_string.slice ( 0, index );
}
}
else
{
cookie_string += LABEL_URL_PAIR_SEPARATOR + page_label_URL;
}
}
- From the cookie string build the current page breadcrumbs.
:
var label_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 );
}
}
- Replace the current page breadcrumb <div> with the new one.
:
breadcrumbs_div.parentNode.replaceChild ( bread_crumbs,
breadcrumbs_div );
- Replace the old breadcrumb cookie string with the new one.
:
erase_cookie ( "breadcrumb" );
create_cookie ( "breadcrumb",
cookie_string,
0 );
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
5. Downloads
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:
#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
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
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
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
I have presented a method that provides developers with the ability to generate traversal history breadcrumbs dynamically.
8. Development Environment
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
01/27/2020 | Original article |