Jquery: Running Events As You Leave a Page

June 29th, 2008

I recently attended “An Event Apart, Boston.” While there someone asked for my thoughts on a jquery problem which intrigued me. Mostly I was interested because I couldn’t come up with a solid solution in the 5 minutes or so we had left before the next session started.

The troubling spot for me was executing an animation on a link click before redirecting to the href of the link. I’ve seen the $("#content").animate({ opacity:1}, 100); trick to add delays within a queue, but for some reason that doesn’t work when applied to the document.

My proposed solution actually uses settimeout() which is what was suggested by the person asking the question. My question to the greater community is: Is there a better way to delay a call to document.location.href?

Here’s my proposed jquery page redirect delay solution.

Zipped for easy consumption

One note: I’m using jquery to move the content box off to the left and then bring it back in, so that people without javascript enabled will still see the content, but it seems to add an annoying flash of content sometimes. To avoid this, just remove the jquery line $("#content").css('left', '-3000%'); and add left: 3000%; to your css file.

Modifying a styleswitcher to make a language switcher.

March 26th, 2005

This is a modified version of Alistapart’s PHP styleswitcher. The idea was to take a site that is both in English and Spanish and allow someone to make a semi-permanent decision on which language they would like to use. This project came through a friend of mine, and with few details, so I figured I’d pick a method that could be easily changed if it didn’t fit his current setup.

Here is a rudimentary example if you want to see it in action. (After you’ve chosen your language just try going to the other page in the address bar!)

My idea was that the english pages would be named something like index_en.html and the spanish pages would be named index_es.html. This means that to switch from one to the other, all you need to do is swap out the bit at the end of the filename. (You could modify this so that it swapped out a directory name instead, and then you could have a /en/ directory and a /es/ directory in the path name and essentially maintain two different folder structurers.) Okay, enough hypotheticals. Here is how to do it.

The first page you need to build is a modified version of the alistapart styleswitcher. Place this code in a file called switcher.php:
< ?php
setcookie ('sitelang', $set, time()+31536000, '/', 'yourdomain.com', '0');
header("Location: $HTTP_REFERER");
?>

This simply takes an argument passed to it through the url bar and redirects the user back to the same page but with a cookie set that contains the information passed to it. (Don’t forget to change the domain name.)

Next you need to put the following snippets of code a the very top of each of your pages, one for the Spanish pages and one for the English pages:

< ?php
if ($sitelang == '_en') {
$location = $_SERVER["PHP_SELF"];
$goto = str_replace("_es", "_en", "$location");
header("Location: $goto");
die();
}
?>

The first one goes in the top of your Spanish pages, an simply checks to see if the cookie says you should be on an english page, if it does, it replaces the _es in the page you are currently on with _en and then redirects you to the English page.
< ?php
if ($sitelang == '_es') {
$location = $_SERVER["PHP_SELF"];
$goto = str_replace("_en", "_es", "$location");
header("Location: $goto");
die();
}
?>

The English version works exactly the same way, except that it replaces _en with _es.

Finally you need to have some way for your browsers to choose their preferred language. In this case I chose to pass the argument through the URL, but you could create a form on your page that had a submit button and some radio buttons to make your choice. The following two links should be present somewhere on every page.
Oprima aquí para español!
<br />
Click here for English!

The first line lets you choose Spanish as your language and the second lets you choose English.
Sorry if my Spanish is a little out of practice. . .
Post a comment if you find this useful.

Update 3-28-05

After a little more discussion about the specifics of what my friend wanted, we came to the conclusion that while this works really well, it wasn’t quite what he was looking for.

The site he was looking to use this is actually 3 sites, where the first is a splash site for the other two.
So, the final decision was made to have the splash site allow a user to choose their language and to choose if that language is permanent or not. Were still working out the specifics, but here’s a way to set the language and forward people to the correct url based on their decision.

The index page for the splash site would contain the following code:
< ?PHP
if ($sitelang == '_es') {
header("Location: http://www.thespanishsite.org");
die();
}
if ($sitelang == '_en') {
header("Location: http://www. thesenglishsite.org");
die();
}
?>

Then within the body you need a form:
<form action="switcher.php" method="post" name="radio">
<label for="set">Español</label>
<input name="set" id="set" type="radio" value="_es" checked="checked" />
<br />
<label for="set">English</label>
<input name="set" id="set" type="radio" value="_en" />
<br />
<label for="length">Remember this setting</label><input name="length" id="length" type="checkbox" value="year" />
<br />
<input type="submit" value="Set Language"/>
</form>

Then the switcher needs to be changed so that it knows about temporary and long term cookies:
< ?php
if ($length == 'year') {
setcookie ('sitelang', $set, time()+31536000, '/', 'yoursplashdomain.com', '0');
}
else {
// this cookie just needs to survive long enough to get the user to the right place
// one time.
setcookie ('sitelang', $set, time()+60, '/', 'yoursplashdomain.com', '0');
}
header("Location: $HTTP_REFERER");
?>

That should do it.
You can try out a demo here.

Rediscovering JavaScript

January 30th, 2005

It’s been a long time since I played around with JavaScript. I was pretty much under the impression it was dead and basically made editing pages more difficult. When I would go back and try and update pages that I had written long ago with a smattering of script in the header, and some strewn throughout to make things happen, I basically found that it was just too much work for the benefit it provided. With CSS I could accomplish 90% of what I used to do with JavaScript and it was much easier to read and maintain.

I think I’m a born again JavaScript fiend. After recently discovering the great site Unobtrusive Javascript, I’m once again sold. This site makes it easy to separate the script from the CSS from the HTML, and all work harmoniously together.

After playing around with their method of separating CSS and JS from the HTML I was finally able to elegantly solve an issue I’ve been wanting a solution to for years. I love how gmail does the whole stack of cards thing with your email. But more than that I love how they show and hide parts of the page arbitrarily. When you are reading a message there is a nice little reply box down at the bottom that when you put your cursor in to write a reply gets bigger, but doesn’t refresh the page so you can continue to type. And when you want to look back through some old messages all you do is click on the stack of cards and viola there you go, your old messages are right there.

My dilemma was more simplistic. I need to make a large form that I wanted to display on a single page, but not force people to scroll down through the whole thing, and I didn’t want people to have to click through sections of it because I want people to go right where they need to go without having to navigate a menu if they don’t want to. And I wanted to allow people to check over the entire form before submitting it, so they could fill in the whole thing at once if they wanted to, or could submit just parts, and through the wonders of MYSQL they could come back later and finish up.

The solution I had in mind was to show the headings for the sections of the form, and then allow people to click on the header to expand or collapse the section of the form. In addition I wanted a way to expand and collapse the whole form so that if you wanted to jump around from one section to another you could with minimal scrolling.

JavaScript to the rescue.

Using the basic function provided by the fine folks over at Unobtrusive Javascript I was able to make a page where all h2 tags turn into buttons that expand and contract their sibling divs. Then after some trial and error as I got my mind wrapped around what they were doing, I was able to take h3 tags and make them expand and contract all of the divs that immediately proceed h2 tags. The beauty is that all the CSS and all of the JavaScript are in their own files, no body tag onload() functions, no extra classes or id’s even. When CSS is needed for a specific application it is called and generated by the javascript, and the javacript file itself tells the HTML page to load it when the window loads, so all you have to change in your HTML is to include a single line telling it where to find the JavaScript file.

Alright, enough talk here are the three files: (note this is part of a php file so the values for the form fields are self populating with php code, get rid of it if you don’t want this.)

First the HTML File:

<html>
<head>
</head>
<body>
<h3>Collapse All Sections</h3>
<form action="processupdates.php" method="post">
<h2>Personal Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="First" size="30" maxlength="60" tabindex="1"/>
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="last" size="30" maxlength="60" tabindex="2"/>
</div>
<h2>Other Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="<? echo $line["FirstName"]; ?/>" size="30" maxlength="60" tabindex="1">
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="<? echo $line["LastName"]; ?/>" size="30" maxlength="60" tabindex="2">
</div>
<h2>More Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="<? echo $line["FirstName"]; ?/>" size="30" maxlength="60" tabindex="1">
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="<? echo $line["LastName"]; ?/>" size="30" maxlength="60" tabindex="2">
</div>
<br />
<input type="submit" id="Save Application" value="submit"/>
</form>
<h3>Collapse All Sections</h3>
</body>
</html>

Next the CSS:

body{
background:#f8f8f8;
color:#333;
font-family:Arial, Helvetica, sans-serif;
}
h2{
font-size:110%;
font-weight:normal;
width: 40em;
}
h3{
font-size:110%;
font-weight:normal;
width: 40em;
}
.hidden{
display:none;
}
.shown{
display:block;
}
.trigger{
background:#ccf;
cursor:n-resize;
}
.open{
background:#66f;
cursor:s-resize;
}
.hover{
background:#99c;
}

Finally the JavaScript:

function collapse()
{
//Collapses and expands siblings of h2 tags in html file
if(!document.createTextNode){return;}
var heads=document.getElementsByTagName('h2');
for(var i=0;i<heads .length;i++)
{
//Figure out what should be opened and closed.
var tohide=heads[i].nextSibling;
while(tohide.nodeType!=1)
{
tohide=tohide.nextSibling;
}
//open all parts that should be open, and set their parents to be ready to close them
cssjs('add',tohide,'shown')
cssjs('add',heads[i],'trigger')
heads[i].tohide=tohide;
//Make them act more like links so the user gets a response
heads[i].onmouseover=function()
{
cssjs('add',this,'hover');
}
heads[i].onmouseout=function()
{
cssjs('remove',this,'hover');
}
heads[i].onclick=function()
{
if(cssjs('check',this.tohide,'hidden'))
{
cssjs('swap',this,'trigger','open');
cssjs('swap',this.tohide,'hidden','shown');
} else {
cssjs('swap',this,'open','trigger');
cssjs('swap',this.tohide,'shown','hidden');
}
}
}
var mainhead=document.getElementsByTagName('h3');
for(var i=0;i<mainhead.length;i++)
{
//run through and make h3's into close and open all links
cssjs('add',mainhead[i],'trigger');
this.innerHTML = 'Collapse all Sections';
mainhead[i].onmouseover=function()
{
cssjs('add',this,'hover');
}
mainhead[i].onmouseout=function()
{
cssjs('remove',this,'hover');
}
mainhead[i].onclick=function()
{
heads=document.getElementsByTagName('h2');
if(cssjs('check',this,'trigger'))
//This runs through and opens all sections cleaning up the h2's so that they are ready to close again
{
for(var j=0;j<heads.length;j++)
{
if(cssjs('check',heads[j],'trigger'))
{
cssjs('swap',heads[j],'trigger','open');
cssjs('swap',heads[j].tohide,'hidden','shown');
}
}
for(var l=0;l<mainhead.length;l++)
{
//Makes sure that all h3s are ready to close the sections
mainhead[l].innerHTML = 'Expand all Sections';
cssjs('swap',mainhead[l],'trigger','open');
}
} else {
//This runs through and closes all sections cleaning up the h2's so that they are ready to open again
for(var k=0;k<heads.length;k++)
{
if(cssjs('check',heads[k],'open'))
{
cssjs('swap',heads[k],'open','trigger');
cssjs('swap',heads[k].tohide,'shown','hidden');
}
}
for(var m=0;m<mainhead.length;m++)
{
//Makes sure that all h3s are ready to re-open the sections
cssjs('swap',mainhead[m],'open','trigger');
mainhead[m].innerHTML = 'Collapse all Sections';
}
}
}
}
}
function cssjs(a,o,c1,c2)
{
//Applies action a to object o by adding, removing, swapping, or checking for classes c1 or c2.
switch (a){
case 'swap':
o.className=!cssjs('check',o,c1)?o.className.replace(c2,c1):o.className.replace(c1,c2);
break;
case 'add':
if(!cssjs('check',o,c1)){o.className+=o.className?' '+c1:c1;}
break;
case 'remove':
var rep=o.className.match(' '+c1)?' '+c1:c1;
o.className=o.className.replace(rep,'');
break;
case 'check':
return new RegExp('b'+c1+'b').test(o.className)
break;
}
}
//make it all happen when the page loads
window.onload=collapse;

Migrating PHP

November 3rd, 2004

I think the server switch over is just about complete, I have a few parts of my gallery to upload yet. While most things have been working for a while the little stylsheet switcher was failing because the new server has a different version of PHP installed and old variables weren’t working. Took me a while to figure out why Safari was refusing to go to a null address. I was under the impression it was supposed to be re-directed back to where it was. Seems $HTTP_REFERER isn’t good enough any more, you need $_SERVER[’HTTP_REFERER’]. In addition on my old server I could simply use the variable name of an item passed through the url so somepage.php?variable=true, could be referred to as $variable. No longer. Now it has to be gotten with $_GET[’variable’]. I guess that is a good thing because you really might not want your variables to have values when you think they are empty, but for crying out loud, that took me way too long to figure out.

Everyday is a learning experience. . . . election day being one of the biggest.