Summary: I want to launch new windows for PDFs and External Websites without having to add anything into my (x)HTML – a pure behavior that should be separated from my core structure. There have been many attempts at tutorials on this, but I’ve never been happy. Here is my attempt – check out the finished demo.
How do you handle launching new windows with a strict DOCTYPE?
This is a pretty old topic considering that target=”_blank” was wiped out of the (x)HMTL specifications for a strict DOCTYPE a few years ago now. This question is still popping up on forums here and there and I thought I would give my two cents anyway.
Stop copy and pasting tutorials – they’re only meant to convey ideas
I see many people using a script that was published on Sitepoint a couple years back, New-Window in a Standards-Compliant World. Just like all tutorials, it’s conveying an idea and not the end all solution to the problem – same thing with this tutorial. Website owners should be using this idea and applying it to their needs – not copy and pasting! The idea behind the Sitepoint script is that you would add rel=“external” to any outside links, javascript would look through all the links in the document for that and set the target to “_blank”. I’ll take a similar approach with a few modifications.
Why even launch a new window?
To me, there is only one reason to spawn a new window – when you’re linking to a PDF. Usability tests show that users still need them in new windows, and it’s not like I can do anything about users being confused. Jakob Nielson says “Open New Windows for PDF and other Non-Web Documents”.
There are still people out there that want all external websites to launch a new window and it’s tough to argue with them no matter how many studies you throw at them. Check out Beware of Opening Links in a New Window for why we shouldn’t be forcing new windows.
Am I just trying to pass validation? – NO!
There is also a lot of confusion around why would “inject” invalid code just to pass validation? That’s not what we’re doing. We want new windows to be an enhancement to the experience of browsing. Forcing new windows gives screen readers a problem among other problems, hence the reason this was taken out of (x)HTML Strict. Keeping this behavior out is in many ways addressing accessibility, and for all of us purists, all behavior should be separate from structure – It’s basic web standards.
Warn users
For the sake of demonstration, I will include external websites and PDFs – but will make sure that users and user agents are warned that it is happening. How will they be warned? You can simply warn users by placing some text in the link’s title attribute and using a graphic to visually show that a new window will appear.
The Plan
Here’s what I want my script to do:
- Look for all the PDFs in my content (not my navigation and recent posts, etc.)
- Look for all links to outside sites (without the use of the rel attribute)
- Change the target of these links to “_blank”
- Append a “new window” graphic by adding a span just after the text
- Add a warning into the Title attribute of the ‘a’ tag.
Easy enough.
The Script for PDFs
Let’s get started by creating a function and first grabbing all the links in the page – as always, I’m going to start with some error handling.
function newWindows(){ if(!document.getElementById) return; if(!document.getElementsByTagName) return; if(!document.createElement) return; var myLinks = document.getElementsByTagName('a'); }
Already I see two things wrong with this. First, there have to be links on the page in order for this to work. Second, it’s looking for every single link on my page. There is no reason why I would need to waste resource grabbing everything when I know certain areas of my page are always my own links. So lets check that links exist and only grab the links that are in my main content area (this is the only spot that I’ll ever put external links)
var contentArea = document.getElementById('mainContent'); var myLinks = contentArea.getElementsByTagName('a');
There, now I have all the links I want to check through. Lets start a loop and look to see if any of the links are PDFs. I’ll use the match() method to see if any of the href’s contain “.pdf”. For this bit of script, I’m going to throw up an alert that says “I found a PDF” if there is one.
for (i=0; i<myLinks.length; i++){ var linkURL = myLinks[i].getAttribute('href'); if (linkURL.match('\.pdf') == '.pdf') { alert("I found a pdf") } }
So now that it has found the PDF, there are a few things that I would like to do with the link. I’d like to add in a title attribute that says “Open PDF in New Window” to warn the screen readers, add an icon to warn everyone else, and force the new window. For the Icon, I’m going to create a span in the link at the very end to accommodate IE’s quirkiness with links that break to a new line (Read my post on New Window Script Redux for more on this).
Lets go through the links to change the title and target.
for (i=0; i<myLinks.length; i++){ var linkURL = myLinks[i].getAttribute('href'); if (linkURL.match('\.pdf') == '.pdf') { myLinks[i].setAttribute('title', 'Open PDF in a New Window'); myLinks[i].target="_blank"; } }
Now, lets add in that new span and give it a style to pull in the image.
var newSpan = document.createElement('span'); newSpan.className = "pdfLink"; newSpan.innerHTML = " "; myLinks[i].appendChild(newSpan);
So this is the If statement in it’s entirety:
if (linkURL.match('\.pdf') == '.pdf') { myLinks[i].setAttribute('title', 'Open PDF in a New Window'); myLinks[i].target="_blank"; var newSpan = document.createElement('span'); newSpan.className = "pdfLink"; newSpan.innerHTML = " "; myLinks[i].appendChild(newSpan); }
And this is the style I used to apply the icon:
.pdfLink { margin-right:3px; padding-right:19px; background: url(../img/pdfLink.gif) right center no-repeat; }
That’s all I need
For me this is all I need to do for my site. I don’t plan on launching windows for all external sites. Here is the finalized code – just throw the function into an onload event:
function newWindows(){ if(!document.getElementById) return; if(!document.getElementsByTagName) return; if(!document.createElement) return; if(!document.getElementsByTagName('a')) return; var contentArea = document.getElementById('mainContent'); var myLinks = contentArea.getElementsByTagName('a'); for (i=0; i<myLinks.length; i++){ var linkURL = myLinks[i].getAttribute('href'); if (linkURL.match('\.pdf') == '.pdf') { myLinks[i].setAttribute('title', 'Open PDF in a New Window'); myLinks[i].target="_blank"; var newSpan = document.createElement('span'); newSpan.className = "pdfLink"; newSpan.innerHTML = " "; myLinks[i].appendChild(newSpan); } } } window.onload = newWindows;
Extending the Script to External Links
For those of you that do plan on launching new windows for your external sites, I’ll keep going. In addition to looking for the pdfs, I’d like to also see if any of these links have a url that’s not my own. I’m going to first set ‘myURL’ as the first line of the function. This way I don’t have to hunt through the code and I can re-purpose this easily on other sites.
myURL = 'willworkforart.net';
I’ll use an else if statement and, for now, alert that we’ve encountered an external site:
else if (linkURL.match(myURL) != myURL){ alert('Found an external link'); }
Let’s do the same thing that we did with the pdfs – Add the Title tag, add the span and the style for the icon and force the new window. Here’s the finished function:
function newWindows(){ if(!document.getElementById) return; if(!document.getElementsByTagName) return; if(!document.createElement) return; myURL = 'willworkforart.net'; if(!document.getElementsByTagName('a')) return; var contentArea = document.getElementById('mainContent'); var myLinks = contentArea.getElementsByTagName('a'); for (i=0; i<myLinks.length; i++){ var linkURL = myLinks[i].getAttribute('href'); if (linkURL.match('\.pdf') == '.pdf') { myLinks[i].setAttribute('title', 'Open PDF in a New Window'); myLinks[i].target="_blank"; var newSpan = document.createElement('span'); newSpan.className = "pdfLink"; newSpan.innerHTML = " "; myLinks[i].appendChild(newSpan); } else if(linkURL.match(myURL) != myURL){ myLinks[i].setAttribute('title', 'Open Link in a New Window'); myLinks[i].target="_blank"; var newSpan = document.createElement('span'); newSpan.className = "newWindow"; newSpan.innerHTML = " "; myLinks[i].appendChild(newSpan); } } } window.onload = newWindows;
Add the finished styles:
.pdfLink { margin-right:3px; padding-right:19px; background: url(../img/pdfLink.gif) right center no-repeat; } .newWindow { margin-right:3px; padding-right:15px; background: url(../img/newWindow.gif) right center no-repeat; }
Fin.
That’s pretty much it – take a look at the demo – I hope we’ve all learned something.
nice solution man. i’ll admit i’m guilty of occasionally using the “_blank” just because i need the user to stay on a given page. in those instances though, standards compliance isn’t really an issue since it’s mostly internal. good stuff though!