Link to toggle div on opening new page

Hi guys. I don’t mind admitting I’m not too good with all this code. I tend to just copy and paste and then tinker until it works. Hence my problem now.

the site I’m working on is here: OLD RECTORYLAND.com

You’ll notice that I’ve successfully implemented toggling of different <div>s depending on what language is selected from the drop-down box at the upper right. Here’s my existing javascript for this:

 <script type="text/javascript">
// code for risk dropdown 
function toggleSubmit(obj){

	count=0
	while(document.getElementById("d"+count)){
	document.getElementById("d"+count).style.display="none"
	count++
	}
	document.getElementById("d"+obj.selectedIndex).style.display="block"

}
</script>		  	 

This is all well and good, but the links in each <div> all link to the same pages, regardless of language. What I want to do now is specify that when the links are clicked, they continue to open the same pages, but when opened, open different <divs> within those same HTML docs.

So that code something like this:

<div style="float: left; width: 155; padding: 6px;" align="left">
				<a href="military.html" style="text-decoration:none" onclick="toggleDiv('d2');"><div style="background-color: #DFF488;">
				<Font face="Arial" color="#330000">
				<small>&nbsp;
				MILWROL
				<BR></small></div>
				<img src="mil.jpg" width="155" height="140" border="0">
				</a></div>

should open <div id=“d2”> within the page ‘military.html’.

Only it doesn’t, because I have no idea what I’m doing.

I trust I make myself obscure.

I’d also like it if the page could “remember” what language was selected when the user clicks the back button, again to avoid having to reselect their language on every page.

Can anyone suggest an answer to the first problem, if not the second? Thanks.

The FragmentIdentifier is used so that the page scrolls to the identified location.


<a href="military.html#d2">...</a>

If the target page needs to take action to reveal that section, it can check location.hash to get ‘#d2’ from which it can get the id


var id = '';
if (location.hash) {
    id = location.hash.substr(1);
}
if (id) {
    // show on page load
}

Thanks. I changed the code in both files accordingly (still using them as a test case) but it didn’t seem to make any difference…

 <!--------MENU---->
			 <script type="text/javascript">
// code for dropdown 
function toggleSubmit(obj){

	count=0
	while(document.getElementById("d"+count)){
	document.getElementById("d"+count).style.display="none"
	count++
	}
	document.getElementById("d"+obj.selectedIndex).style.display="block"

}
var id = '';
if (location.hash) {
    id = location.hash.substr(1);
}
if (id) {
    // show on page load
}
</script>	

in both index.html and military.html, and

<a href="military.html#d2" style="text-decoration:none">

as the link from the first to the second.

The page still opens with the ‘English’ div showing, and the ‘welsh’ one not. Also, if I use a hash to open the page at a specific location, won’t that preclude the page opening at the very top - instead displaying the content from a point scrolled halfway down, or wherever the new <div> starts? If it had worked, that is. I think I’ve missed something :confused:

When you do use the location.hash to manage things on the page, you can also use window.scroll(0,0) to position things to the top, typically in a setTimeout event.

Another alternative is to pass info via the querystring instead of the hash, which might work better depending on what your page is doing.

Can you show us what you’re doing, with a test page perchance?

Since the page still functions adequately despite not working how I want it to, I’ve uploaded the test pages (index.html and military.html) to the website, so they’re here: OLD RECTORYLAND.com [I]and[/I] [url=http://www.woofkitty.co.uk/3972/military.html]OLD RECTORYLAND.com

I’m using the welsh or British section for testing things out, so if you scroll down to that in the html (about line 637 in index, and 551 in military; the javascript is at about 115 in both) you’ll see what it is I’m doing.

Before you go any further with this idea, you need to know that it’s incredibly wrong to load an HTML page that contains the same content in up to five languages, only to show one of them. It is an incredible waste of bandwidth, and your load times will suffer dramatically.

Internationalization is not a job that javascript does well, but using ajax to load separate sections can have its place.

I’ll put together a simple example where each language is in its own separate file, and the web page builds itself by bringing together those separate files.

Needless to say though, this is not a job that the client-side should be doing. Server-side with PHP or some other type of server-side language is where this type of work should be done.

But, as it’s 3am now, I’ll run up a client-side example that does a better job that what I think you have been intending to do.

Thanks Paul. Like I said, I’m more of a tinkerer than an expert. PHP is just as scary as Javascript!

Accessibility for no-script situations

First up, we need to keep in mind that the page should still be accessible when scripting is not available. We are storing the different language parts in separate files, such as military-english.html and military-prydeinig.html, so the most accessible technique is to link to those pages. That allows people without scripting to get to the content they need, and when scripting is available it can replace those links with the retrieved content.


<div id="content">
    <a href="military-english.html">English content</a>
    <a href="military-anglisc.html">Anglisc content</a>
    <a href="military-prydeinig.html">Prydeinig content</a>
    <a href="military-francais.html">Francais content</a>
    <a href="military-deutsch.html">Deutsch content</a>
</div>

Selecting the language

The above links are only for a “no scripting” situation though.

With the select box, we give it values so that the script can easily find the appropriate file. The language suffix on the above filenames match the below option values, so that the script can automatically determine the correct file to retrieve.


<select id="language"> 
    <option value="english">English</option> 
    <option value="anglisc">Anglisc</option> 
    <option value="prydeinig">Prydeinig</option> 
    <option value="francais">Francais</option> 
    <option value="deutsch">Deutsch</option> 
</select> 

Retrieve the file with Ajax

When someone selects Prydeinig we want the content section to be replaced with that which is in the military-prydeinig.html file. Ajax is used for that, for which there’s a very good book called Bulletproof Ajax. When we go to its [url=“http://bulletproofajax.com/code/”]code and examples we can see that Chapter 4 contains code to retrieve content from an HTML page and display it within the existing page.

The code to do that (slightly modified in the parseResponse() function to work with our content section) is:


function getHTTPObject() {
  var xhr = false;
  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        xhr = false;
      }
    }
  }
  return xhr;
}

function grabFile(file) {
  var request = getHTTPObject();
  if (request) {
    request.onreadystatechange = function() {
      parseResponse(request);
    };
    request.open("GET", file, true);
    request.send(null);
  }
}

function parseResponse(request) {
  if (request.readyState == 4) {
    if (request.status == 200 || request.status == 304) {
      var content = document.getElementById("content");
      content.innerHTML = request.responseText;
    }
  }
}

Put scripts in a separate file at the bottom

Now is a good time to bring up that scripting code should not be embedded in the HTML code for the page. Instead, it should be in a separate script file that you load at the end of the body with:


<script src="js/script.js"></script>
</body> 
</html> 

That allows the page to load even faster than if the script were in the head, and the script can be used on more than one page without needing to download the script again.

Which content file to retrieve?

As the page loads, we attach an onchange event to the select field that retrieves the content.

While it’s possible to use ‘military-’ + language + ‘.html’ to create the file name, I want the script to be more flexible so that it can be used on more than just the military page, so I’ll instead use a regular expression to find the filename, split it, and then append the language on to the filename.


var select = document.getElementById('language');

select.onchange = function () {
    var language = this.options[this.selectedIndex].value,
        filename = location.href.match(/([^\\/]*).html/)[1];
    grabFile(filename + '-' + language + '.html');
    return false;
}
select.onchange();

So if the page was history.html and the chosen language is Francais, it will try to load the content from history-francais.html

The last part triggers the onchange event, so that the content can be automatically retrieved as the page loads.

Load the page with a desired chosen langauge

So the page now automatically retrieves the content when you select different language settings. How can we get the page to load with a language of our choice?

We can load the page with a querystring such as ?lang=prydeinig
The script can then set the select box value to whatever it provided as that value. If the select box contains a matching value, it will set the select box to that value.
Then the typical page-load checking of the select value can take place.

JavaScript doesn’t provide a way to easily interpret the querystring value, so we’ll roll our own solution:


function getQueryString() {
    var qs = {},
    terms = location.search.substr(1).split('&'),
    i,
    split;
    for (i = 0; i < terms.length; i += 1) {
        split = terms[i].split('=');
        qs[split[0]] = split[1];
    }
    return qs;
}

Now, in-between defining the select variable and checking the select box, we can get the querystring and set the select box to that which is specified in lang.


var select = document.getElementById('language'),
    qs = getQueryString();

if (qs.lang) {
    select.value = qs.lang;
}


select.onchange = function () {
    ...

So now we can load the page as history.html?lang=prydeinig and the page will set the select box to Prydeinig, and then loads the content from history-prydeinig.html to show within the page.

Test page to demonstrate the code in action

I’ve put up a test demo of this using your history page as an example, over at http://paulwilkinsdev.110mb.com/i18n/military.html

I’ve edited the quotation for the French and German languages too, to help you see things working in action.

Next steps?

The next step to take from here would be for the page to remember your chosen language. That can be done by storing the chosen language as a cookie value, so that other pages on the site can automatically retrieve that chosen language. But that can be for later on once the basic setup is in place.

Thanks a lot, Paul! I really appreciate the effort you’ve put into explaining this for me. Thank goodness for patient people like you :slight_smile:


function getHTTPObject() {
  var xhr = false;
  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        xhr = false;
      }
    }
  }
  return xhr;
} 

Huh, I’ve seen many iterations of this, but this is the first time I’ve seen the empty var declared “false” right away before redefining it.

Why is that?

For example I’ve used something like this one:


        var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        if (xhr !==null) {
    //do ajaxy stuff...
        }
...


Hmm… Have you any idea why OLD RECTORYLAND.com is not working and index.html is only working intermittently? To be more specific, the content doesn’t show up. Sometimes re-starting the browser does it, but refreshing doesn’t seem to change it either. I’ve scanned the code and can’t see anything wrong. The JS script is loaded… hmph

…in fact index.html doesn’t operate load the script either, when reached from oldrectoryland.com as opposed to through its folder on woofkitty.co.uk. I can’t figure out why this should be so (it’s just framed, I think…)

That’s setting a default value for it. That way if you are on a web browser that does not support any kind of Ajax technique, it will return false to let the script know that it can’t be done.


var request = getHTTPObject();
if (request) {
    ...
}

I don’t know about intermittently, but what I do know is that the content in military-english.html and military-prydeinig.html and the other language files don’t currently exist on your server. You need to create those files and load them with content.

Odd - they’re definitely there. I can see them in the FTP… they even have the right names…

Can you navigate to them via the on-page link? That will definitely confirm whether they are publicly available or not.

It looks like they are working now.

You might want to use proper HTML entities for the French translation, such as é and ê

Well that’s one problem sorted - for some reason they were stored as .htm instead of .html. The other still exists, but as it seems to be a problem with my web forwarding I don’t expect you can help. When it forwards to the folder where I’ve located the index file it doesn’t load properly - and now I’ve changed it to forward to the index file itself it’s ceased to work completely. Gah.

Ok; here’s the issue now. When opening oldrectoryland/index.html, the script loads. When merely loading [URL=“http://www.oldrectoryland.com/”]www.oldrectoryland.com, it doesn’t.

This is essentially the issue I had before, before it was occluded by the Curious Case of the Altered File Extensions. Any ideas?

When the filename is missing, it’s not able to automatically assume which filename to use eh?

That’s one of the reasons why I was earlier saying that javascript is not really appropriate for the task.
Let’s help it though to automatically do that.

Here is the existing section of code:


var language = this.options[this.selectedIndex].value,
    filename = location.href.match(/([^\\/]*).html/)[1];
grabFile(filename + '-' + language + '.html');
return false;

Let’s get the match separately, then check if a match was found. If a match was found, we can retrieve the found expression. Otherwise, we can provide it with a default filename instead.


var ...
    match = location.href.match(/([^\\/]*).html/),
    filename = (match && match[1]) || 'index.html';

Here’s how the script should look with the update in place:


var language = this.options[this.selectedIndex].value,
    match = location.href.match(/([^\\/]*).html/),
    filename = (match && match[1]) || 'index.html';
grabFile(filename + '-' + language + '.html');
return false;