THIS IS A SPA (I GUESS)

Intro and basic code
This is a single-page application (SPA). A SPA is a website that fits on a single page. All necessary code is retrieved with a single page load or dynamically loaded and added to the page as necessary. The page does not reload at any point in the process unless it is manually refreshed. The location hash or other means can be used to provide the perception and navigability of separate 'pages' and to make them bookmarkable.
As a SPA is fully loaded in the initial page load - page regions being replaced or updated (on demand) afterwards - SPA systems have a slower first page load than traditional multi-page websites. This is compensated by the fact that subsequently replacing or updating page regions is a much faster process than replacing or updating entire pages.
SPA has been adopted by several javascript frameworks such as AngularJS, Ember.js, Meteor.js and ExtJS. But these frameworks are overkill in regards to what is needed for a SPA to function properly. In fact, we can do without them and create a SPA with only a few lines of code (copy the code below and paste it into a file to see it at work). This dispenses us from learning new things (Angular, Ember etc.) that take a lot of time and aren't needed after all:

<!-- In the head: -->
<script>
function content_change()
{
/* FILLING THE CONTENT-AREA (=A DIV HAVING ID='content_area' ON IT) WITH CONTENT FROM A HIDDEN DIV. THE HIDDEN DIV MUST HAVE AN ID ON IT, WHICH CAN BE FREELY CHOSEN. USE JQUERY IF YOU LIKE*/
document.getElementById('content_area').innerHTML = document.getElementById(location.hash.substring(1,location.hash.length)).innerHTML
/*$('#content_area').html($(location.hash).html())*/
}
window.onhashchange=content_change;
</script>

<!-- Navigation items and hidden divs in the body: -->
<a href="#page1">Page 1</a>
<a href="#page2">Page 2</a>
<!-- etc. -->

<!-- These links reference hidden 'pages' (sections) like: -->
<div style="position: absolute; display: none; ">
<div id="page1">
This is a single-page application (SPA). A SPA is a website that fits on a single page. All necessary code is retrieved with a single page load or dynamically loaded and added to the page as necessary. The page does not reload, and new pages are not loaded, at any point in the process. The location hash or other means can be used to provide the perception and navigability / bookmarkability of separate 'pages' in the application.
</div>
<div id="page2">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
</div>
<!-- etc. -->

<!-- The region in the body where the script inserts a hidden div (a 'page') depending on the href value of the clicked link. Something like: -->
<div id="content_area" style="position: relative; width: 100%; height: auto"></div>

<!-- At the end of the body section: -->
<script>
/* Load the first 'page' at page entrance. If the ID on the first 'page' (section) is not page1, replace '#page1' below with your own ID (preceded by the hash symbol) */
if(window.location.hash==''){window.location.replace('#page1');}

/* Execute the function given above in the head when page loads */
setTimeout("content_change()",0)
</script>

The hrefs in this code fragment do what hrefs in traditional multi-page websites do: they tell the browser to execute some action in response to what's in the address bar. In traditional sites, the href value is used to load entire pages. In this SPA, the part after the hash symbol in the URL is used to identify a portion of the page (a hidden div, normally) whose innerHTML is then inserted into the content area (a div having id="content_area") via function content_change(). For instance, href="#some_page" tells the browser to look for a div on the page having id="some_page" and to transfer its innerHTML to another div that has id="content_area" on it. Of course, some_page is not a real page, but a portion of the SPA-page.

You are free to change the href value in a link and the name of the corresponding ID on a hidden div. If you do so, make sure that the ID on the hidden div and the href value in the link look the same. For instance, if you replace the href value in the first link above with #our_collection, then put id="our_collection" for the first hidden div. (In this particular case, you should also replace if(window.location.hash==''){window.location.replace('#page1');} at the end of the body section with if(window.location.hash==''){window.location.replace('#our_collection');}.

The code given above can be enriched with all sorts of javascript and css methods. We don't care here about the css (we'll leave that up to you) but will focus on a couple of javascript enrichments that you might be interested in.

Further reading here.
A script for highlighting current menu items in a SPA
In a SPA, hightlighting 'current menu items' cannot be done width the help of code written to newly loaded pages, because there's only one page: the SPA. A SPA typically uses javascript-code 'inside it' to do what we want when a new 'page' is loaded via a click on a link. The following code, which we add to function content_change() given on page 1 (which is not a real page but a page region, as we know by now), does the job:

/* HIGHLIGHTING THE SELECTED MENU ITEM */
var x = document.getElementsByClassName("menu_item");
var i;
for (i = 0; i < x.length; i++)
{x[i].removeAttribute('style');
if(x[i].href.substring(x[i].href.lastIndexOf('#'), x[i].href.length)==location.hash) //if the href value for an active link in the navigation menu is identical to the ID on the current section ('page')
{/*CSS for the menu link. Just an example. Modify if you want*/
x[i].style.color='red';
x[i].style.fontStyle='italic';
}}

Of course, this only works if the menu links are <a> elements (common practice in web developement; only <a> elements have a href attribute) that have class="menu_item". So now our (complete) code looks like this (copy the code and paste it into a document to see it at work):

<!-- In the head: -->
<script>
function content_change()
{
/* FILLING THE CONTENT-AREA (=A DIV HAVING ID='content_area' ON IT) WITH CONTENT FROM A HIDDEN DIV. THE HIDDEN DIV MUST HAVE AN ID ON IT, WHICH CAN BE FREELY CHOSEN. USE JQUERY IF YOU LIKE*/
document.getElementById('content_area').innerHTML = document.getElementById(location.hash.substring(1,location.hash.length)).innerHTML
/*$('#content_area').html($(location.hash).html())*/

/* HIGHLIGHTING THE SELECTED MENU ITEM */
var x = document.getElementsByClassName("menu_item");
var i;
for (i = 0; i < x.length; i++)
{x[i].removeAttribute('style');
if(x[i].href.substring(x[i].href.lastIndexOf('#'), x[i].href.length)==location.hash) //if the href value for an active link in the navigation menu is identical to the ID on the current section ('page')
{/*CSS for the menu link. Just an example. Modify if you want*/
x[i].style.color='red';
x[i].style.fontStyle='italic';
}}
}
window.onhashchange=content_change;
</script>

<!-- Navigation items and hidden divs in the body: -->
<a class="menu_item" href="#page1">Page 1</a>
<a class="menu_item" href="#page2">Page 2</a>
<!-- etc. -->

<!-- These links reference hidden 'pages' (sections) like: -->
<div style="position: absolute; display: none; ">
<div id="page1">
This is a single-page application (SPA). A SPA is a website that fits on a single page. All necessary code is retrieved with a single page load or dynamically loaded and added to the page as necessary. The page does not reload, and new pages are not loaded, at any point in the process. The location hash or other means can be used to provide the perception and navigability / bookmarkability of separate 'pages' in the application.
</div>
<div id="page2">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
</div>
<!-- etc. -->

<!-- The region in the body where the script inserts a hidden div (a 'page') depending on the href value of the clicked link. Something like: -->
<div id="content_area" style="position: relative; width: 100%; height: auto"></div>

<!-- At the end of the body section: -->
<script>
/* Load the first 'page' at page entrance. If the ID on the first 'page' (section) is not page1, replace '#page1' below with your own ID (preceded by the hash symbol) */
if(window.location.hash==''){window.location.replace('#page1');}

/* Execute the function given above in the head when page loads */
setTimeout("content_change()",0)
</script>

Further reading here.
Hooking javascript events to 'page transitions'
We can force events to happen when the content region of the page is updated (when a 'page transition' occurs) by adding javascript equivalents of lines like the following to function content_change() (see preceding pages for this function):

do something here in order to cancel do this and do that in the lines below (only needed if do this and do that are supposed to make changes to the page);
if(location.hash=='#page1'){do this};
if(location.hash=='#page2'){do that};

etc.
For instance, if we wanted a red background for the first 'page', a blue background for the second one (we wouldn't want that, but this is just an example) and the default background-color for the other 'pages', we would do:

document.body.style.background=''; //two successive single quotes
if(location.hash=='#page1'){document.body.style.background='red'};
if(location.hash=='#page2'){document.body.style.background='blue'};

The first line is needed to make sure that the 'other pages' don't inherit one of the background-colors given in the second and third lines.

On this SPA, we've used this method to obtain different header images for different 'pages'. Here's what we did (header_img is the ID on an image that we put at some appropriate place of our site):

document.getElementById('header_img').src='';
document.getElementById('header_img').style.minWidth='';
if(location.hash=='#page1'){document.getElementById('header_img').src='first_image.jpg'}
if(location.hash=='#page2'){document.getElementById('header_img').src='second_image.jpg'; document.getElementById('header_img').style.minWidth='35%'}
if(location.hash=='#page3'){document.getElementById('header_img').src='third_image.jpg'; document.getElementById('header_img').style.minWidth='35%'}
/*etc.*/

Using this method allows us to do everything we can do on a traditional multi-page website.

Further reading here.
Loading external files into the SPA
The hidden divs whose contents are loaded into the content area in response to a click look like this (see here and here, where it says: <!-- These links reference hidden 'pages' (sections) like: -->):

<div style="position: absolute; display: none; ">

<div id="page1">
This is a single-page application (SPA). A SPA is a website that fits on a single page. All necessary code is retrieved with a single page load or dynamically loaded and added to the page as necessary. The page does not reload, and new pages are not loaded, at any point in the process. The location hash or other means can be used to provide the perception and navigability / bookmarkability of separate 'pages' in the application.
</div>

<div id="page2">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>

<!-- etc. -->

</div>

In this example, the contents of the divs (the ones that have an ID on them) contain a couple of lines of simple text only. But we may want to add complicated HTML to the divs together with lots of more text, images etc. In that case, the source of our (single!) SPA-page may rapidly become hardly readable - the more so because it contains all the info and code of the entire site - and it might seem a better idea to put the contents of the divs in separate files. These external files must be loaded into the divs, of course. There are two easy ways to do this:

jQuery load():
<div style="position: absolute; display: none; ">

<div id="page1">
<div id='some_external_file'><script>$('#some_external_file').load('some_external_file.html')</script> </div>
<!--This only works if you have in the head: <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>-->
</div>

<div id="page2">
<div id='some_external_file2'><script>$('#some_external_file2').load('some_external_file2.html')</script> </div>
<!--This only works if you have in the head: <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>-->
</div>

<!-- etc. -->

</div>

Auto-rezising seamless iframe embed:
<div style="position: absolute; display: none; ">

<div id="page1">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'some_external_file.html')"></iframe>
<!--This only works if you have this in the head-->
</div>

<div id="page2">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'some_external_file2.html')"></iframe>
<!--This only works if you have this in the head-->
</div>

<!-- etc. -->

</div>

To see the jQuery method at work, copy the code below and paste it into a document (which will serve then as a SPA). The code is identical to the one given earlier except for the lines in red. The document references two external files: external1.html and external2.html, so make sure to also create two documents having these names. Put lots of text, images etc. in them. Don't put javascript-code in the external files, because that might yield unexpected results. It's better to put all the javascript in the SPA-file if you use jQuery for inserting external files into the SPA (see here on how to handle the javascript).

<!-- In the head: -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
function content_change()
{
/* FILLING THE CONTENT-AREA (=A DIV HAVING ID='content_area' ON IT) WITH CONTENT FROM A HIDDEN DIV. THE HIDDEN DIV MUST HAVE AN ID ON IT, WHICH CAN BE FREELY CHOSEN. USE JQUERY IF YOU LIKE*/
document.getElementById('content_area').innerHTML = document.getElementById(location.hash.substring(1,location.hash.length)).innerHTML
/*$('#content_area').html($(location.hash).html())*/

/* HIGHLIGHTING THE SELECTED MENU ITEM */
var x = document.getElementsByClassName("menu_item");
var i;
for (i = 0; i < x.length; i++)
{x[i].removeAttribute('style');
if(x[i].href.substring(x[i].href.lastIndexOf('#'), x[i].href.length)==location.hash) //if the href value for an active link in the navigation menu is identical to the ID on the current section ('page')
{/*CSS for the menu link. Just an example. Modify if you want*/
x[i].style.color='red';
x[i].style.fontStyle='italic';
}}
}
window.onhashchange=content_change;
</script>

<!-- Navigation items and hidden divs in the body: -->
<a class="menu_item" href="#page1">Page 1</a>
<a class="menu_item" href="#page2">Page 2</a>
<!-- etc. -->

<!-- These links reference hidden 'pages' (sections) like: -->
<div style="position: absolute; display: none; ">
<div id="page1">
<div id='external1'><script>$('#external1').load('external1.html')</script> </div>
<!--This only works if you have in the head: <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>-->
</div>
<div id="page2">
<div id='external2'><script>$('#external2').load('external2.html')</script> </div>
<!--This only works if you have in the head: <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>-->
</div>
<!-- etc. -->
</div>

<!-- The region in the body where the script inserts a hidden div (a 'page') depending on the href value of the clicked link. Something like: -->
<div id="content_area" style="position: relative; width: 100%; height: auto"></div>

<!-- At the end of the body section: -->
<script>
/* Load the first 'page' at page entrance. If the ID on the first 'page' (section) is not page1, replace '#page1' below with your own ID (preceded by the hash symbol) */
if(window.location.hash==''){window.location.replace('#page1');}

/* Execute the function given above in the head when page loads */
setTimeout("content_change()",0)
</script>

As for the iframe-embed method, do as indicated above for the jQuery method except that this time you should use the following code:

<!-- In the head: -->
<script>
function seamless_iframe(the_iframe, the_file)
{
for (var i = 0; i < document.getElementsByTagName('style').length; i++)
{
the_iframe.contentWindow.document.getElementsByTagName('head')[0].appendChild(document.getElementsByTagName('style')[i].cloneNode(true)) // Comment this line if you don't want the iframed document to inherit the styles given in the head for the main document
};
the_iframe.style.width=the_iframe.parentNode.clientWidth+'px';
the_iframe.style.height=0;
the_iframe.style.borderWidth=0;
the_iframe.scrolling='no';
the_iframe.style.verticalAlign='top';
the_iframe.style.padding=0;
the_iframe.style.margin=0;
the_iframe.style.height=Math.max(the_iframe.contentWindow.document.body.scrollHeight, the_iframe.contentWindow.document.body.offsetHeight, the_iframe.contentWindow.document.documentElement.clientHeight, the_iframe.contentWindow.document.documentElement.scrollHeight, the_iframe.contentWindow.document.documentElement.offsetHeight) -5 +'px';
if(the_iframe.contentWindow.location.href=='about:blank'){the_iframe.contentWindow.location.href=the_file};
}
</script>
<script>
function content_change()
{
/* FILLING THE CONTENT-AREA (=A DIV HAVING ID='content_area' ON IT) WITH CONTENT FROM A HIDDEN DIV. THE HIDDEN DIV MUST HAVE AN ID ON IT, WHICH CAN BE FREELY CHOSEN. USE JQUERY IF YOU LIKE*/
document.getElementById('content_area').innerHTML = document.getElementById(location.hash.substring(1,location.hash.length)).innerHTML
/*$('#content_area').html($(location.hash).html())*/

/* HIGHLIGHTING THE SELECTED MENU ITEM */
var x = document.getElementsByClassName("menu_item");
var i;
for (i = 0; i < x.length; i++)
{x[i].removeAttribute('style');
if(x[i].href.substring(x[i].href.lastIndexOf('#'), x[i].href.length)==location.hash) //if the href value for an active link in the navigation menu is identical to the ID on the current section ('page')
{/*CSS for the menu link. Just an example. Modify if you want*/
x[i].style.color='red';
x[i].style.fontStyle='italic';
}}
}
window.onhashchange=content_change;
</script>

<!-- Navigation items and hidden divs in the body: -->
<a class="menu_item" href="#page1">Page 1</a>
<a class="menu_item" href="#page2">Page 2</a>
<!-- etc. -->

<!-- These links reference hidden 'pages' (sections) like: -->
<div style="position: absolute; display: none; ">
<div id="page1">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'external1.html')"></iframe>
<!--This only works if you have this in the head-->
</div>
<div id="page2">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'external2.html')"></iframe>
<!--This only works if you have this in the head-->
</div>
<!-- etc. -->
</div>

<!-- The region in the body where the script inserts a hidden div (a 'page') depending on the href value of the clicked link. Something like: -->
<div id="content_area" style="position: relative; width: 100%; height: auto"></div>

<!-- At the end of the body section: -->
<script>
/* We need this in order for function seamless_iframe (head) to continue to function properly when the window is resized */
var windowWidth = window.innerWidth;
window.onresize=function()
{
if(windowWidth != window.innerWidth)
setTimeout("location.reload()",10); return
}
</script>

<script>
/* Load the first 'page' at page entrance. If the ID on the first 'page' (section) is not page1, replace '#page1' below with your own ID (preceded by the hash symbol) */
if(window.location.hash==''){window.location.replace('#page1');}

/* Execute the function given above in the head when page loads */
setTimeout("content_change()",0)
</script>

The advantage of the iframe embed over the jQuery method is that code put in the iframed document cannot influence or conflict with code in the main document (the SPA file) and vice versa. Notice here that if we want the iframed file to be completely independent of the main document, we should comment or remove the line that says the_iframe.contentWindow.document.getElementsByTagName('head')[0].appendChild(document.getElementsByTagName('style')[i].cloneNode(true)) in function seamless_iframe.

Further reading here.
SPA and Search Engine Optimization
In our system, http://example.com/#bla1 and http://example.com/#bla2 are two different 'pages' (rather: page region states) each having its own content. Now, search bots always ignore the hash portion of a URL when indexing a page. So search bots see the same thing in both cases (http://example.com), meaning that http://example.com/#bla1 and http://example.com/#bla2 cannot be indexed as two separate 'pages'.

Some time ago, Google made a proposal for how webmasters can tackle this problem: if we use '#!' (the hashbang) instead of the simple hash ('#'), then the Googlebot will recognize http://example.com/#!bla1 and http://example.com/#!bla2 as two different things. Unfortunately, Google is the only search engine that currently supports the 'Google Crawling Scheme', so hashbang URLs will only be indexed by Google.

On this site, we don't use hashbang URLs; we use the normal hash without '!'. So there is no way for search engines to crawl our 'pages', unless... we find a way to circumvent the problem. We explained here how we can insert external pages into our SPA page. Let's repeat the relevant parts of this method here:

jQuery:
...
<a class="menu_item" href="#page1">Page 1</a>
<a class="menu_item" href="#page2">Page 2</a>
...
<div style="position: absolute; display: none; ">
<div id="page1">
<div id='external1'><script>$('#external1').load('external1.html')</script> </div>
</div>
<div id="page2">
<div id='external2'><script>$('#external2').load('external2.html')</script> </div>
</div>
...
</div>

Iframe:
...
<a class="menu_item" href="#page1">Page 1</a>
<a class="menu_item" href="#page2">Page 2</a>
...
<div style="position: absolute; display: none; ">
<div id="page1">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'external1.html')"></iframe>
</div>
<div id="page2">
<iframe frameborder="0" scrolling="no" src="about:blank" onload="seamless_iframe(this, 'external2.html')"></iframe>
</div>
...
</div>

Now suppose there's a way to tell the browser what's inside the div having id="page1" and inside the div having id="page2" (above) on the sole basis of code written to external1.html and external2.html, not on the basis of the lines written in the SPA page. Then loading these pages 'in isolation' would yield the same results as clicking (tapping) on the links in the SPA page. What's more, we would be able to submit both external1.html and external2.html to search engines, which could index them as separate pages. Indexing problem for pages having a hash symbol in the URL solved!

But how do we do that? What code should be written to external1.html and external2.html? The way we have organized our SPA allows for an easy solution. We simply add to the end of the body section of external1.html, whose content should be the content of 'page1', see above:

<script>
function no_orphans()
{
/*Dont't allow pages to show as 'orphans' (without the SPA page). The main page is called 'index.html' here. You can change that, in which change the following line must be modified accordingly*/
if(top.window.location.hash=='')top.window.location.replace('index.html#page1')
}
no_orphans()
</script>

And to the end of the body section of external2.html, whose content should be the content of 'page2' (see above) we add:

<script>
function no_orphans()
{
/*Dont't allow pages to show as 'orphans' (without the SPA page). The main page is called 'index.html' here. You can change that, in which change the following line must be modified accordingly*/
if(top.window.location.hash=='')top.window.location.replace('index.html#page2')
}
no_orphans()
</script>

Try it. Create index.html, write the code for loading external files into the SPA page (= index.html, in this case) to it (jQuery method or iframe-embed method, code given here), then create external1.html and external2.html into which you insert the scripts (function no_orphans) given above and finally open these files 'in isolation'. The results will be as expected.

Further reading here.
The End
Let's summarize:
  • There are many frameworks for single-page applications out there: AngularJS, Ember.js, Meteor.js, ExtJS etc. But they force us to learn things that take a lot of work to make our SPA function as it should. So we created a SPA without a framework, that is, a SPA that works with code we're already familiar with.
  • In our SPA, the part after the hash symbol in the URL is used to identify a portion of the page (a hidden div, normally) whose innerHTML is then inserted into the content area (a div having id="content_area") via a function content_change().
  • This function also contains lines for highlighting 'current menu items' and hooking events to 'page transitions' (updates of the content area in the SPA page).
  • We allow the hidden divs whose contents are used to update the content area in the SPA page to contain code that makes it possible to pull content from external pages into the divs.
  • We provided a method for making the content area of our SPA crawlable.

Arie Molendijk, mesdomaines.nu.