PDA

View Full Version : Open select links in new window.. cookie?



Luna C
03-23-2005, 08:40 AM
Open select links in new window
http://www.dynamicdrive.com/dynamicindex8/newwindow3.htm

Is it possible to add a cookie to remember the visitors preference?

I found a somewhat similar script on this site (http://willmaster.com/possibilities/archives/wmp20030114001.shtml) ( code example page (http://willmaster.com/possibilities/demo/NewWindowCheckbox/WithCookieExample.html)) that uses cookies, but it can't set select links (All links on the site are set to either _blank or not.. even on-site links and others that are best in the same window).

A combo of the 2 of them is exactly what I need.

I've tried (for hours and hours lol), but with very limited javascripting ability, and no cookie experience whatsoever, I have to admit that the cookies have beat me.

So, does anyone know how to add cookies to the Dynamic Drive Script?

mwinter
03-23-2005, 03:17 PM
Open select links in new window
http://www.dynamicdrive.com/dynamicindex8/newwindow3.htm

Is it possible to add a cookie to remember the visitors preference?Give the code below a whirl. It's a complete rewrite of the original, so there are a few changes to its operation.

Firstly, the class names nwindow and nwindowcontainer have been expanded to new-window and new-window-container. You can alter these names if you like by editing the regular expressions in the getLinks function.

The code which looks for class names no longer requires exclusive use of the class attribute. That is, you can specify multiple class names on an element (if it belongs to more than one class) without the other names interfering.

At present, the checkbox is inserted wherever you include the code. If you'd rather include the checkbox yourself, I'll have to make alterations (unless you feel up to it ;)).

The cookie is currently set to expire after one year. You can (and probably should) set it to expire sooner. Replace the get/setFullYear calls with something more appropriate like


d.setDate(d.getDate() + 7);which would allow the cookie to expire after a week. In addition, the cookie will affect all pages that use this code. It is possible to make this work on a page-by-page basis, but again that would require modification.

The current frame/link target is _blank. That can be changed by editing the target variable.

The id attribute value of the checkbox is new-window. You'll want to make sure that no other elements use this value.

The code will work in most modern browsers, and it should even work in IE4, but I have made no attempt to make it work in NN4. It's also only undergone mild testing, but I'm confident there won't be any problems.

Hope that helps,
Mike



(function() {
var d = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function lz(s, n) {var p = ''; s = String(s); n -= s.length;
while(p.length < n) {p += '0';} return p + s;
}

Date.prototype.toGMTString = function() {
return [
d[this.getDay()],
', ',
lz(this.getDate(), 2),
'-',
m[this.getMonth()],
'-',
lz(this.getFullYear(), 4),
' ',
lz(this.getHours(), 2),
':',
lz(this.getMinutes(), 2),
':',
lz(this.getSeconds(), 2),
' GMT'
].join('');
};

if(!Array.prototype.push) {
Array.prototype.push = function() {
var j = 0, k = this.length, n = arguments.length;
while(j < n) {this[k++] = arguments[j++];}
return k;
};
}
})();

var setTarget = (function() {
var target = '_blank';

function getLinks() {var e = [], r = [];
if(document.getElementsByTagName) {e = document.getElementsByTagName('*');}
if(!e.length && document.all) {e = document.all;}
for(var i = 0, m = e.length, c, l; i < m; ++i) {c = e[i];
if(/(^|\s+)new-window(\s+|$)/.test(c.className) && ('A' == c.tagName)) {
r.push(c);
} else if(/(^|\s+)new-window-container(\s+|$)/.test(c.className)) {l = [];
if(c.getElementsByTagName) {l = c.getElementsByTagName('a');}
if(!l.length && c.all && c.all.tags) {l = c.all.tags('a');}
for(var j = 0, n = l.length; j < n; ++j) {r.push(l[j]);}
}
}
return r;
}

if((document.getElementById || document.all)
&& (document.getElementsByTagName || (document.all && document.all.tags))
&& document.write)
{
document.write([
'<label for="new-window">',
'<input id="new-window" type="checkbox" onclick="setTarget(this);"',
(/(^|;\s*)new-window=1/.test(document.cookie) ? ' checked>' : '>'),
'Open links in a new window',
'</label>'
].join(''));
document.close();

return function(e) {var d = new Date(), l = getLinks(), t = '', u;
if(e && (u = e.checked)) {t = target;}
for(var i = 0, n = l.length; i < n; ++i) {l[i].target = t;}
d.setFullYear(d.getFullYear() + 1);
document.cookie = 'new-window=' + (+u) + ';expires=' + d.toGMTString();
};
}
})();

if(setTarget) {
this.onload = (function() {var l = this.onload;
return function() {var t = null;
if(document.getElementById) {
t = document.getElementById('new-window');
} else if(document.all) {
t = document.all['new-window'];
}
setTarget(t);
if(l) {this.onload = l; this.onload();}
};
})();
}

Luna C
03-24-2005, 07:58 AM
OMG, thank you! It works perfectly.

I have one question, how do I set "_blank" to be the default? ie.. the checkbox loads pre-checked?

(I'd taken a poll, and new windows for offsite links won by a landslide, I just want the option for the other 9%.. which included myself ;) )

I tried a few ideas and they either broke the script entirely or didn't do a thing... Like I said, I'm very, very limited when it comes to javascript. I gave it a good try though :o .

As for the checkbox appearing where the code is, I'm ok with that. Unless I'm missing something, it seemed to work even when it wasn't after the links.

A 'not really important but could be quite handy feature'.. is there a way to have a div class="new-window", but exclude a certain link with class="same-window" or something inside that link? Would save a bit of code on some pages with mixed links. If not, no problem.

And a huge thank you for this! You have no idea how long I've been looking for a script that does this.

mwinter
03-24-2005, 12:07 PM
OMG, thank you!You're most welcome.


It works perfectly.I can't confirm if the persistence (cookie) mechanism works on earlier versions of IE (though it should). I run all versions on the same machine and cookies (amongst other things) don't work properly under this setup (the cookie's written, but can't be read back :confused: ).


I have one question, how do I set "_blank" to be the default? ie.. the checkbox loads pre-checked?There's a new variable, newByDefault alongside target in the new version below (if you search for the text, they'll be the first whole-word occurances). Setting the value to true will check the checkbox if no cookie is found.


(I'd taken a poll, and new windows for offsite links won by a landslide, I just want the option for the other 9%.. which included myself ;) ):D Now if only other developers had that attitude...


I tried a few ideas and they either broke the script entirely or didn't do a thing...If you have other suggests besides the ones in your previous post, by all means let me know.


Like I said, I'm very, very limited when it comes to javascript. I gave it a good try though :o .It's not the easiest code to modify as it's specifically engineered (more or less) to what you asked for originally.


As for the checkbox appearing where the code is, I'm ok with that.I could make it work either way. The current implementation was just marginally easier.


Unless I'm missing something, it seemed to work even when it wasn't after the links.That's correct. It waits until the document has finished loading before modifying the links, though it does preliminary work beforehand. Though the code automatically calls itself from the load event, it will call any other code that was defined there previously.


A 'not really important but could be quite handy feature'.. is there a way to have a div class="new-window",I think you mean new-window-container. Only a elements with a class name of new-window will have their target properties altered.


but exclude a certain link with class="same-window" or something inside that link?Sure! (done)


Would save a bit of code on some pages with mixed links.I can imagine...

Mike


Note: The expiry date is still a year in this code, the current default is to open new windows, and as code has changed in various locations I would recommend you replace the version you have entirely.


var getElementById = null;

(function() {
var d = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function lz(s, n) {var p = ''; s = String(s); n -= s.length;
while(p.length < n) {p += '0';} return p + s;
}

Date.prototype.toGMTString = function() {
return [
d[this.getDay()],
', ',
lz(this.getDate(), 2),
'-',
m[this.getMonth()],
'-',
lz(this.getFullYear(), 4),
' ',
lz(this.getHours(), 2),
':',
lz(this.getMinutes(), 2),
':',
lz(this.getSeconds(), 2),
' GMT'
].join('');
};

if(document.getElementById) {
getElementById = function(id) {return document.getElementById(id);}
} else if(document.all) {
getElementById = function(id) {var r = document.all[id];
if(('number' == typeof r.length) && r.tags) {
for(var j = 0, n = r.length; j < n; ++j) {
if(r[j].id == id) {return r[j];}
}
} else if(r.id == id) {return r;}
return null;
};
} else {
getElementById = function() {return null;};
getElementById.alwaysNull = true;
}

if(!Array.prototype.push) {
Array.prototype.push = function() {
var j = 0, k = this.length, n = arguments.length;
while(j < n) {this[k++] = arguments[j++];}
return k;
};
}
})();

var setTarget = (function() {
var target = '_blank',
newByDefault = true;

function getLinks() {var e = [], r = [];
if(document.getElementsByTagName) {e = document.getElementsByTagName('*');}
if(!e.length && document.all) {e = document.all;}
for(var i = 0, m = e.length, c, l; i < m; ++i) {c = e[i];
if(/(^|\s+)new-window(\s+|$)/.test(c.className) && ('A' == c.tagName)) {
r.push(c);
} else if(/(^|\s+)new-window-container(\s+|$)/.test(c.className)) {l = [];
if(c.getElementsByTagName) {l = c.getElementsByTagName('a');}
if(!l.length && c.all && c.all.tags) {l = c.all.tags('a');}
for(var j = 0, n = l.length; j < n; ++j) {c = l[j];
if(!/(^|\s+)same-window(\s+|$)/.test(c.className)) {r.push(c);}
}
}
}
return r;
}

if((document.getElementsByTagName || (document.all && document.all.tags))
&& document.write && !getElementById.alwaysNull)
{
var cookie = /(^|;\s*)new-window=(\d)/.exec(document.cookie);

if(cookie) {cookie = !!(+cookie[2]);} else {cookie = newByDefault;}
document.write([
'<label for="new-window">',
'<input id="new-window" type="checkbox" onclick="setTarget(this);"',
(cookie ? ' checked>' : '>'),
'Open links in a new window',
'</label>'
].join(''));
document.close();

return function(e) {var d = new Date(), l = getLinks(), t = '', u;
if(e && (u = e.checked)) {t = target;}
for(var i = 0, n = l.length; i < n; ++i) {l[i].target = t;}
d.setFullYear(d.getFullYear() + 1);
document.cookie = 'new-window=' + (+u) + ';expires=' + d.toGMTString();
};
}
})();

if(setTarget) {
this.onload = (function() {var l = this.onload;
return function() {var t = getElementById('new-window');
if(t) {setTarget(t);}
if(l) {this.onload = l; this.onload();}
};
})();
}

Luna C
03-24-2005, 10:47 PM
I could hug you! Thank you so much, this is working exactly as I wanted :D

The "new-window-container" with a few "same-window" is working perfectly as well.

As for it not working in NS4.. lol what does. To be honest if they're still using that they're used to stuff not working. My css excludes them as well. They just get a 'white background, black default text, no layout' css delivered. It's readable, but looks terrible. There's just no way to make a pure css site that looks good for them that isn't bloated for the other 99%. I can respect luddites, but I won't cater to them after a point. ;)

Again, a big huge thank-you. Finally everyone will be happy with the way the links open.

As a side note: This brings me closer to a strict doctype validation as well.. just an iframe to remove now :D

mwinter
03-24-2005, 11:32 PM
As for it not working in NS4.. lol what does.There's no reason why I couldn't make it work, I just really can't be bothered. ;) NN4 is such a PITA to author for, and is so near death, that it isn't worth the effort unless there are a significant number of visitors using it. The only thing I tend to worry about is not crashing the stupid thing, which has happened on occasions - it just can't parse the code properly (it doesn't actually get as far as executing it!)


Again, a big huge thank-you.And as I said before, you're welcome. :)


As a side note: This brings me closer to a strict doctype validation as well.. just an iframe to remove now :DErm, sorry to burst your bubble there, but it wouldn't technically validate. Although the mark-up would be deemed valid, you must remember that no validator checks the actions of scripts[1]. When the script modifies the links, it will add target attributes which are not present of the Strict DTD, so the document tree will in fact be invalid. Similarly, the code writes in an input element wrapped in a label. If this combination is inserted as a child of the body, this too would be invalid as only block-level and script elements can be children of body. The script element would need to be wrapped in a div or similar.

But (and it's a big "but") validation needn't be something to get too worked up over. The most important thing is to use sensible, semantic, well-formed mark-up. If that only validates to a Loose DTD, you've still done better than the vast majority of authors on the Web and it's certainly something to be proud of!

I'm probably not getting that hug now... :(

Mike


[1] Thinking about that, the string '</label>' written into the document should be changed to '<\/label>' if you include the code in the HTML document itself. If you have it in an external file (and you should, really) don't worry about it.

Luna C
03-25-2005, 08:45 AM
Well.. it validates.. if not in the spirit of Strict 4.01.. It squeezes by on a technicality. ;) So yup, you still get a hug. And even with my new squeaky clean doctype it works like a charm. (At least tested in Firefox, Opera and a ie6 anyway.) If I find a problem caused by going strict, I'm ok with reverting back to transitional.

I did find another problem though. It's setting a cookie for each subfolder.

So, I looked around and found out a path needs set (path=/; is the path that I need) for sites with multiple levels.. the problem is that I couldn't find any website that described how to do that, aimed at a beginner level. So I'm not even close to sure as to where in the script that would go, or really how it would be written. So yet again, I came back for help. (Last time I promise)

Lol, you're not going to want that hug if i don't leave you alone soon :p


And yup, I put it in an external. I'm used to that for anything that's used cross-site. php includes and external css have saved me many hours quite a few times.

mwinter
03-25-2005, 10:29 AM
If I find a problem caused by going strict, I'm ok with reverting back to transitional.Always aim for Strict, but if you really need a Transitional feature then use it. There are more important things to worry about than which DTD you validate to, but you should always try to hold yourself to some standard.


I did find another problem though. It's setting a cookie for each subfolder.Edit the string literal that contains ';expires=' to ';path=/;expires=' (or a subdirectory if you don't have your own domain or subdomain). It's near the end of the script.


Lol, you're not going to want that hug if i don't leave you alone soon :pNah. Seriously, it's fine. :)

Mike

Luna C
03-25-2005, 11:39 AM
Ah, so that's where it goes. Working exactly perfect now.

Thanks again :D