Opening new windows with JavaScript, version 1.2

Update: The technique described here is not ideal. Read why in New windows with JavaScript and the target attribute.

My script for opening new windows without using the target attribute became more popular than I thought it would. A lot of people commented on the post, Using JavaScript instead of target to open new windows, with suggestions for improvement.

A few days after the original post, I updated the script (see Opening new windows with JavaScript, version 1.1) , and now it’s time for another update. The news is that the script now uses object literal notation for better portability and lets you choose which attribute-value pair (or pairs) should make links open in new windows.

Let’s start by taking a look at the revised script, jstarget.js:

  1. /*
  2. JSTarget function by Roger Johansson, www.456bereastreet.com
  3. */
  4. var JSTarget = {
  5. init: function(att,val,warning) {
  6. if (document.getElementById && document.createElement && document.appendChild) {
  7. var strAtt = ((typeof att == 'undefined') || (att == null)) ? 'class' : att;
  8. var strVal = ((typeof val == 'undefined') || (val == null)) ? 'non-html' : val;
  9. var strWarning = ((typeof warning == 'undefined') || (warning == null)) ? ' (opens in a new window)' : warning;
  10. var oWarning;
  11. var arrLinks = document.getElementsByTagName('a');
  12. var oLink;
  13. var oRegExp = new RegExp("(^|\\s)" + strVal + "(\\s|$)");
  14. for (var i = 0; i < arrLinks.length; i++) {
  15. oLink = arrLinks[i];
  16. if ((strAtt == 'class') && (oRegExp.test(oLink.className)) || (oRegExp.test(oLink.getAttribute(strAtt)))) {
  17. oWarning = document.createElement("em");
  18. oWarning.appendChild( document.createTextNode(strWarning) );
  19. oLink.appendChild(oWarning);
  20. oLink.onclick = JSTarget.openWin;
  21. }
  22. oWarning = null;
  23. }
  24. }
  25. },
  26. openWin: function(e) {
  27. var event = (!e) ? window.event : e;
  28. if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return true;
  29. else {
  30. var oWin = window.open(this.getAttribute('href'), '_blank');
  31. if (oWin) {
  32. if (oWin.focus) oWin.focus();
  33. return false;
  34. }
  35. oWin = null;
  36. return true;
  37. }
  38. },
  39. /*
  40. addEvent function from http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
  41. */
  42. addEvent: function(obj, type, fn) {
  43. if (obj.addEventListener)
  44. obj.addEventListener(type, fn, false);
  45. else if (obj.attachEvent) {
  46. obj["e"+type+fn] = fn;
  47. obj[type+fn] = function() {obj["e"+type+fn]( window.event );}
  48. obj.attachEvent("on"+type, obj[type+fn]);
  49. }
  50. }
  51. };
  52. JSTarget.addEvent(window, 'load', function(){JSTarget.init("rel","external"," (external website, opens in a new window)");});

I’m not going to explain every line of code in this script. Instead, here is a quick description of how to use it, along with a very simple demo page.

If you look at the last line of the script, you will notice that the JSTarget.init() function takes three parameters (all optional). This is what makes this script more flexible than the previous versions.

Several people commented that they would prefer using the rel attribute to indicate that a link should open in a new window. I do not agree with that, since the rel attribute describes the relationship between the current document and the one being linked to. If the link leads to a document on another site, using something like rel="external" would make sense. If the target document is on the same site though, I don’t think it makes sense to label it as “external”.

Anyway, you can now choose to use the rel attribute if you want to. To do so, call the init() function with two parameters; the name of the attribute to look for and the value that will trigger the script. You can also add a third parameter to change the text that warns the user that the link will open in a new window.

The default setup is to look for a class attribute that contains the value non-html, and insert the text ” (opens in a new window)” at the end of the link. To use that, just initialise the script like this:

  1. JSTarget.addEvent(window, 'load', function(){JSTarget.init();});

To make the script look for rel attributes with the value external and insert the text ” (external website, opens in a new window)”, use the following:

  1. JSTarget.addEvent(window, 'load', function(){JSTarget.init("rel","external"," (external website, opens in a new window)");});

If you want to check for links with class="non-html" or rel="external", you can run the script twice, once with the default setup and once with custom parameters:

  1. JSTarget.addEvent(window, 'load', function(){JSTarget.init();});
  2. JSTarget.addEvent(window, 'load', function(){JSTarget.init("rel","external"," (external website, opens in a new window)");});

There will be problems if a link matches both cases, so avoid that situation (i.e. <a href="/" class="non-html" rel="external">).

I could have used variables inside the script to store the settings. However, since I see myself using this on CMS-driven sites I think being able to specify the parameters from the addEvent function makes it more flexible. In that situation I would move the addEvent() call out of the JS file and let the CMS write it to each page with the correct parameters (for instance using a different warning text for different languages).

As always, any suggestions for further improvement are welcome.

Update (2006-10-14): Ok, I realise now that I should have included a disclaimer about opening new windows since it’s obvious that I cannot expect people to read the entire article, the previous articles, and my comments to get the whole story. So here goes.

Opening new windows should almost always be avoided. There are very few exceptions to that rule. However, sometimes it is beyond your control as a Web developer to enforce that. You can normally choose which DOCTYPE to use though. I prefer using a strict DOCTYPE, which does not allow the target attribute. And that is why I created this script. It can also be argued that opening new windows is behaviour, and as such should be moved to the behaviour layer (JavaScript) anyway.

Posted on October 12, 2006 in JavaScript

Comments

  1. So you’re the reason all those annoying sites keep bugging me with those nasty auto-popups? ;-)

  2. This is a handy script, but it begs the question: why have pop up links in the first place? This takes the control of how the link behaves out of the user’s hands. It’s accessable - which is more than I can say for most new-window links - but I always thought that new-window as a default was a bad idea?

    Too bad there’s no hook for new tab!

  3. Ok, now I will just come off as Mr. Know-it-all, but I say two small things:

    1) You have &amp;&amp; in your example code here, which I guess isn’t the meaning (or is it some kind of XHTML/MIME type thought behind it?).

    2) In the name of consistency, you should either go for ternary expressions ( (check)? true : false ) or if else clauses all over, like you have in one place:

    if (!e) event = window.event;
    else event = e;

    (Man, this comment’s code is probably get so butchered once I post it… :-))

  4. Looks interesting though I always found pages that opened new windows annoying.

    In trying the last link on the demo page not only didn’t I get a new window (yes javascript is enabled, browser is FireFox) but I received an XMP parsing error from the W3 site.

  5. RE:Wd and Jon This is not about deciding the worth/use of popups - its about having an accessible way to do so when the case arises. The debate of using/not using a popup window is for another topic.

    Roger, I like the changes you have made to this. It makes it much more flexible. Using objects helps make things much easier (not just in JS) - so I am sure this will help others in their need to customize the popup.

    Could also be extended to pass parameters if they needed to specify width/height combos for images or something (somewhat like lightbox and others) - again, not a debate of SHOULD you use them - just an option. Not that your script NEEDS that - but having it as an object will make it easy to add the functionality.

    Nice work.

  6. October 12, 2006 by Roger Johansson (Author comment)

    Jon, WD Milner: Like Nate said, this is something that you can use when forced to open links in new windows by circumstances beyond your control. I hate new windows too, but I can’t always talk the powers that be out of using them.

    Robert: The ampersands were a result of double escaping. Fixed now. I also changed the event assignment to a ternary expression - I missed that one. Thanks for keeping an eye on my code ;-).

    WD Milner: Hmm, that’s odd. I’ve tested this in every browser I could lay my hands on with no problems. Are you by any chance using an extension that interferes with window handling?

    Nate: Thanks. I thought of extending the script to take an additional parameter for setting window size, chrome, etc, but decided not to make this any more complicated than it needs to be.

  7. There seem to be a few character encoding problems in your code. Line 14 has an encoded less-than sign and line 16 has two ampersands.

  8. I could have used variables inside the script to store the settings. However, since I see myself using this on CMS-driven sites I think being able to specify the parameters from the addEvent function makes it more flexible.

    Why not use all three? Use Object Literal Notation global variables to store the default values. This allows the developer to change the default values. If you’re really into OOP, then create set methods for each value. Allow as you do now, the ability to pass parameters from the addEvent function.

    1. The developer can use the default values.
    2. The developer can change the default values.
    3. The developer can pass different values from the addEvent function.

    Putting the default values in variables will allow programmers to modify the default values without having to look in the code to find the default values.

  9. Jon: It’s like the goto discussion. But just remember that unless you have a specialised audience the typical user will not be terribly knowledgable. A significant percentage will not attempt to use the right mouse button, so they may actually appreciate the web designer making the choice.

  10. 52 lines of JS just to open new windows? I realise that many sites use so many target attributes that they’d probably total more than 1.8kb but it just seems like a complete bitch to have to do all this?

    Very nicely done though :)

  11. Very nice!

    I will have a closer look into the script when I have time to do so. I’m just wondering about the initialization of the script (or better: about multiple JS-scripts). It uses an event listener to fire a function when the page is loaded… can this event listener be used to initialize other scripts like mint or google analytics? (or any other script for that matter) And what might be the best way to do it? Call a single function which contains all the init-calls? or add multiple calls of JSTarget.addEvent?

    @Dave The Problem is this one: If you want to create standards compliant websites, you have to use a “strict”-doctype. In HTML4 there is still the target-attribute, but it’s gone in XHTML1.x. So to validate your website and still be able to open new windows links… you use this method described in the article.

    There are several other JS-scripts which simply add the target-attribute dynamically to fix this, but that’s just not right. (there is no target-attribute… anymore)

  12. October 13, 2006 by Roger Johansson (Author comment)

    Thomas: Oops, more double-encoded characters. Thanks for catching that. The linked JS-file did not have those problems though.

    Tanny: Well that’s pretty much how it works now, except the default values aren’t stored in global variables. But since you mention it, I’ll probably do that for the sake of clarity. Thanks.

    Johan: If you want to use the same addEvent() function to initialise multiple scripts I’d suggest moving it to a global JS file. I included the function in this script to make it “plug-and-play”.

  13. Nice work! Love the object literal notation, now it nicely blends in with my own personal scripts ;)

    One suggestion though is into creating a namespace for your javascript goodiness (http://www.snook.ca/archives/javascript/javascript_name/), maybe 456BEREA or sth like that?

    wbr, B!

  14. October 13, 2006 by Gerben

    The script doesn’t seem to work ik IE5.5.

    In the regular expression you could just use \b instead of (^|\s) and (\s|$)

    I’m not sure but shouldn’t you use event.preventDefault(); instead of return false;

  15. October 13, 2006 by Kenzie

    This is timely for me; just this week I lost a battle to keep external links from opening in “popup” windows. A script like this will let me keep my dignity. :-) Thanks.

  16. Technically spoken, the “solution” is IMHO nice done. Elegant.

    The thing I’m not understanding, is the “problem”, is it an accessibility matter? In this case you just can add some information (picto, or word or note) to tell visitors that the page will open in a new window.

    Is it about validating? Yes, your page won’t be valid in strict mode. So what? :)

    I understand that it’s a waste of time finding deliberately inserted markup errors when validating in strict mode, but then why not just go Transitional?

    Sorry, I don’t mean to say that your work is worthless… no! really. But I was just wondering about how we, humans, see problems and imagine solutions.

    Thank you! :)

  17. October 13, 2006 by Gerben

    @karim. One advantage of this technique is that if, on a later date, you don’t want the popups you only have to change code on one location.

    Can’t really think of any other advantage to just adding target="_blank".

  18. @karim, Gerben: The target attribute is not allowed in a document that has a strict doctype.

  19. October 13, 2006 by Meint

    @Joe: Then create your own strict doctype that does allow target=”_blank”. I still can’t fathom what the advantage is of losing target attributes in any strict vocabulary. I wish the w3c would create/host a strict doctype that does include target attributes but until then I’ll reintroduce them myself and still use a strict doctype.

  20. October 13, 2006 by Adam van den Hoven

    Roger,

    You suggest that you don’t like the idea of using the rel attribute because it makes no sense to mark an internal link with rel="external" which is absolutely true. However, I think you’re missing the point. And the point is that you should not be opening links in a new window on a whim; there should always be a reason to open a page in a new window.

    So as a policy on my site, I only open external links and contextual help links in a new window (as an example) so I would use:

    JSTarget.addEvent(window, 'load', function(){JSTarget.init(
        "rel",
        "(external)",
        " (external website, opens in a new window)");});
    JSTarget.addEvent(window, 'load', function(){JSTarget.init(
        "rel",
        "(contexthelp)",
        " (contextual help; opens in a new window)");});
    

    This allows one to use real semantics in their markup. If the only reason you’re opening a window is “I think its a good idea” you haven’t thought very carefully about your site.

    Especially if you’re using a CMS.

  21. @Joe: Yes I know, but I thought Transitional permits it. Am I wrong? I mean, why streeve to make pages validate in strict mode? That’s what I actually don’t understand… “Pages”, after all, are served for “humans” to read on browsers…

    @Gerben: thank you! I admit it, I didn’t get that vision of things. And I really love that -sorry for the oldish word- modularity :) Make one change and let it flow…

    But why would we change the behavior of pages for which we decided to open in a new window? Isn’t it an accessibility and sometimes, they say, a marketing “responsible” decision?

  22. ok, I removed the “Talkback” extension in Firefox and I no longer get the XML errors, however it doesn’t open a new window either - I’m haunted grin

  23. October 14, 2006 by Roger Johansson (Author comment)

    Bramus: Thanks. Interesting idea about creating a namespace - I’ll look into that.

    Gerben: Not sure why it doesn’t work in IE 5.5. It doesn’t work in IE/Mac either, so users of obsolete browsers can consider themselves lucky :-).

    Using \b in the regexp would not work for attribute values that contain hyphens, since they are word boundaries.

    Not sure about event.preventDefault().

    karim: Yes, it is a matter of usability and accessibility. Always let the user know before opening a new window.

    So what if it doesn’t validate? You don’t do a lot of quality control on large sites, do you? It takes a lot more time to sift through validation errors to manually find the ones that are “real” errors.

    Adam: I do not open new windows “on a whim”. I only allow it if I am forced to by the client or in the very few cases where a new window may actually be acceptable. See my update at the end of the article for more.

    WD Milner: That’s odd. Well, be happy that you’re not getting a new window ;-).

  24. I just want to know why is the target attribute bad?

  25. October 15, 2006 by henrah

    Gustav: If for no other reasons, the target attribute is bad because it has no semantic value, repeats information which (on a well-designed page) is already present in other attributes; and requires a value starting with an underscore.

    I mean, come on. An underscore? That ain’t pretty

  26. henrah: All this work becouse a underscore isn’t pretty? 52 lines of javascript is better than underscore?

    Seems like a hard way to do something simple. But, true - underscore isn’t pretty at all.

  27. It didn’t work in netscape8, after i’ve tweaked it, then it worked out fine.

  28. Roger. There are two ways you can describe all scripts. Better or best. This is the best. Good job :)

  29. I don’t know what you guys are talking about, because I hate it when a site is linking to an outside site and does not open in a new window. I think it is ridiculous to say that a site shouldn’t open an external site in a new window, and I always thought that was best for the User Experience while browsing a site.

    Who wants to be reading an article or looking over a website and wants to check out the link posted and leave the site they are on. I understand with pop ups and what not.

    Thank you for this script, I was using something similar but was not as easy to attach to inidividual links.

  30. Great script, Roger. I especially like way it deals with modifier keys nicely.

    Thinking about this script got me thinking about the arguments about adding the target attribute to STRICT documents and if that was valid or not. So, I put together an article.

    Keep up the great stuff.

  31. October 16, 2006 by Roger Johansson (Author comment)

    Gustavs: The target attribute is not allowed in strict DOCTYPES.

    milo317: What did you do to make it work in Netscape 8? (Ok, I admit that I haven’t tested this script - or anything else - in Netscape 8. The main reason is that there is no Mac version.)

    Dustin: Thanks! :-D

    Dan: Most usability and accessibility experts will disagree with you. Here are two:

  32. October 16, 2006 by Roger Johansson (Author comment)

    milo317: I just downloaded Netscape 8.1.2, and the script works fine with both rendering engines. What settings did you use that broke the script?

  33. Thanks for this always like messing with more code and seeing the output! Found vua dugg and blogged!

  34. I agree that popup windows should be used sparingly, but here is a much cleaner approach (IMHO) that i’ve used. The one benifit I see that your script does have is that it sets everything up from an init script; but I don’t think that encourages using it sparingly.

    http://jehiah.com/archive/prototype-powered-popup-script

    <a href="path/to/page" onclick="return Popup.open({url:this.href});">link title</a>

  35. I will try ! thnx

  36. I hate popup windows and links that open in new windows for no valid reason. That said, I accepted long ago that they are a necessary evil, when the client is not prepared to accept a puritanical approach. I’m confused as to why the complicated solution is necessary: the dynamic assigment of target=”” through Javascript is perfectly acceptable, as use of the link target attribute is still valid, provided it’s set with functional Javascript and not through direct html assignment. (target=”_blank”).

  37. October 18, 2006 by Roger Johansson (Author comment)

    Jehiah: Using inline event handlers should be avoided. If you want to change the behaviour of those links, you have to go back and edit the HTML, which can be a lot of work on a large site. In addition, that will neither warn the user that a new window will open nor give you any hooks for styling.

    Mark: If you want to use a JavaScript-created target attribute you can modify the openWin() function.

  38. October 20, 2006 by analgesia

    @Jehiah. Why not just use:

    <a href="path/to/page" onclick="return !window.open(this.href);">
      link title (opens in new window)</a>
    

    It’s not very pretty, but if you just need one or two place on the entire site it might be acceptable.

  39. The example with the link to a pdf file doesn’t work for me (IE7, no beta!). It displays the same error as a simple target=”_blank” (without any JavaScript) would:

    Problems with this Web page might prevent it from being displayed properly or functioning properly….

    Line: 1 Char: 1 Error: Missing object Code: 0 URL: http://www.456berea…/jstarget.pdf

  40. Correction: as it seems, this is an Acrobat problem. Adobe has provided an update. See also http://www.adobe.com/support/techdocs/331246.html

  41. I don’t know if anyone has mentioned it yet - and please don’t think I’m nagging, Roger - but the script on the demo page seems very slow to respond compared to other scripts I’ve used, which are half the size? Is it worth saying that this script is a good starting point for people’s own solutions? I think it perhaps tries to cover too many bases?

  42. October 24, 2006 by Roger Johansson (Author comment)

    dotjay: Hmm. I don’t see any slowness here, not even when running Windows XP in Virtual PC on a 4 year old Mac. What’s your configuration?

  43. Roger - please excuse me. I think it must be something with Firefox having about 40 tabs open!

    Your demo page was taking around 10-15 seconds to open the new window, which seemed abnormally slow. I just tested the script I usually use and it’s pretty much the same.

  44. October 25, 2006 by Roger Johansson (Author comment)

    dotjay: Hehe, yeah I’ve noticed that when you have massive amounts of tabs open it will slow down both Firefox and Safari. I try to limit myself to around 10 tabs.

  45. If I want to control the size of the new browser window and control the amount of chrome what would I do?

  46. November 8, 2006 by Roger Johansson (Author comment)

    Matt: You would need to modify the script to accept a string with size and chrome settings. It’s beyond the purpose of this script.

  47. This is amazing article Roger…I can’t stop visiting your site for more practical web tips.

    I have a suggestion for the use of your script. Personally, I prefer to use a small icon to denote an external link instead of text. The problem with this is that with CSS turned off, readers won’t know the difference.

    I found this article at Max Design http://www.maxdesign.com.au/presentation/external/ that discusses how to display an icon for CSS users and text for non-CSS users. It involves a simple span tag, which I believe you can wrap around your “external link” text in the initialization function. I haven’t had time try it out myself yet, so I hope this idea works. I will post again if there are problems when I implement it.

    Keep up the good work again!

  48. November 28, 2006 by Roger Johansson (Author comment)

    Jeff: That technique looks safe enough. I suppose I could change my script to insert the span element and leave it up to everyone to decide whether to style it or not.

  49. This is why I love jQuery, it allows me to write a small line of code to do the same thing:

    $("a.external, a.pdf").attr({target: "_blank"});
    

    Any link with the class “external” or “pdf” will now open in a new window, without using a target attribute in the markup.

  50. December 29, 2006 by Roger Johansson (Author comment)

    Eric: Sure, if you’ve drunk the JS library kool-aid, go ahead. Just don’t forget that jQuery (or any other JS library) will need to be loaded for that to work. Also, that single line will not do what this script does (warn the user that a new window will open and prevent a new window from opening if a modifier key is pressed).

  51. Thanks. I’m trying to stay with strict doctype — I think it will be important in the future but mostly like the challenge. Javascript, though is beyond me. I cannot remove the addition of “opens a new window” text and have the script work, but don’t like the look. How can I change the appearance of that text?

  52. Never mind. I found that “small” works. Don’t like it though, so am going to use a blank space in that part of the javascript.

  53. December 30, 2006 by Roger Johansson (Author comment)

    csleh: To remove the warning about a new window being opened, change this:

    JSTarget.init(“rel”,”external”,” (external website, opens in a new window)”)

    to this:

    JSTarget.init(“rel”,”external”,”“)

    However I don’t think doing so is a good idea since the user won’t know that a new window is about to be opened. That is bad for both usability and accessibility.

  54. April 18, 2007 by java-java

    does it validate on w3c :? try{window.open(‘http://www.eaxmple.com/index.html’,’windowname’, ‘status=yes,scrollbars=yes, resizable=yes,width=400,height=300’)} catch(e){alert(‘i've blocked your pop-up’)}

    i thought it never fails :\

  55. April 19, 2007 by user

    Great script, one last thing I would like it to do is allow html for <img src="popup.gif"> instead of (external website, opens in a new window)

  56. @ user (#55)

    I did exactly that, all by myself, which is no mean feat because I’ve no idea what I’m doing with JS.

    You can find the script on this post. Comments and corrections more than welcome.

  57. OK, I am confused. I am using a chgoto.js dropdown list in the page http://mte0055.50webs.com/PictureMain.htm . I am trying to get the second selection (Ericas Graduation) to open in a new tab, because I am going to have it open the Picasa web album instead of building a page for all of the pictures, like I have done in the past. But, I can’t seem to figure out how to make it work. I have put your jstarget.js in the Head and in the Body. Neither one seems to make it work. Can anyone see what I have done wrong?

  58. I saved a copy of the site to a folder on the computer and it doesn’t bring the jstarget.js file, just the chgoto.js file…

  59. This does not seem to work with anchor tags that already have an onclick attribute attached, since it will destroy what is already there.

    I have tried many approaches, to no avail. Is there any way to get around this without separating the current onclick functionality into another function to be evaluated?

  60. June 6, 2007 by Roger Johansson (Author comment)

    Jospeh: If you change line 20 from this:

    oLink.onclick = JSTarget.openWin;
    

    to this:

    JSTarget.addEvent(oLink, 'click', JSTarget.openWin);
    

    it should (though I haven’t tested it) play nice with other onclick handlers on the links.

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.