Putting Links in New Windows

Frankly, I’m not a big fan of popup links in most cases. When I want to open a link in a new window, I know how to, and big egos like mine don’t generally dig sites telling us how to surf the web.

Nevertheless, it is rather convenient in several situations. In particular, on a messageboard where people post links that are relevant to the conversation, you might want to view the link in a new window so that you can easily close it and return to the discussion, or have both open to comment easily. Certainly, very little is quite as annoying as internal links that open up a bunch of new windows, effectively breaking the back button - so the <base target="_blank" /> “solution” is as unsatisfactory as it is dirty. And, if you loop through the DOM applying events, it should know to avoid internal links.

Speaking of that forum, quite often people ask how to make external links pop up in new windows, while keeping the internal links within one window. I had taken a crack at it a few days ago, but I wasn’t really happy with the solution. It uses the target="_blank" attribute to get the desired effect, which is not compliant with XHTML strict. More importantly, it uses JavaScript to create XHTML to do something that JavaScript should have done in the first place, which just rubs me the wrong way - compliant or not.

A better solution:

  • Loop through the DOM applying a relationship to each A tag, using the REL attribute, depending on the URL of the HREF attribute. This way, the data is stored in the correct semantic format - the script-modified XHTML still just says what it is, and the browser and client-side scripting dictates what it does.
  • Don’t add REL attribs to links that already have them - let’s assume that the author is smarter than the script. ;)
  • Any links that have an ‘external’ relationship, and any links point outside of our specified domain and path will trigger a window.open event to the target, and disable the “normal” click event.
  • If the window.open event fails, due to blocking popups or whatever, we still want the link to open normally. Many sites do not take this additional step, and I always find it a little disconcerting to have to control-click to view the link. Rather than making them allow pop-ups, let’s just turn the pop-ups into normal links for those folks.

It would be nice to have a checkbox to enable/disable this feature, and even better if that checkbox stored a cookie to remember the user’s preference for next time. You could also use an attribute selector in your CSS to style the links differently according to their REL attribute. But that’ll have to wait for version 2 of this trick.

Special thanks to Porter Glendinning for the prototype of this idea, Scott Andrew lePera for his lovely addEvent function that I use all over the place.

Read on for the code and explanation.

The file can be downloaded from the test page.

Globals

var myBaseUrl = 'localhost/tests|geocities.com/bryht|isaacschlueter.com';
var poppinRel = 'external';

Nothing too exciting here. poppinRel tells the script which REL value it should use for popups. (External seems to be the value of choice, but anything else will work, too.) myBaseUrl contains the base URL(s) that should not load in new windows. Because it’s regexed, you can separate multiple values with a pipe. An http:// is not necessary, and will cause problems if there’s more than one base URL. Trailing slashes = bad. I set these at the top of the file so that it’s easy to change them if necessary.

makeEmPoppin()

function makeEmPoppin() {
  // Fetch all the links in the document.
  if( document.links ) var links = document.links;
  else var links = document.getElementsByTagName(’a');
  // loop through all links.
  for (var i=0;links[i];i++) {
    var a = links[i];
    // If the element doesn’t have an href, skip it.
    if (!a.href) continue;
    // handle it.  muchas gracias, Regular-Expressions.info.  
    if ((!a.href.indexOf(’http://’)
      && !a.href.match(’^http://([^.]+[.])?(’ + myBaseUrl + ‘)’))
      || a.getAttribute(’rel’) == poppinRel) {
      if( !a.rel ) a.setAttribute(’rel’,poppinRel);
      addEvent(a,’click’,PopWin);
    }
  }
}
This is the initialization function, which runs on window.load. The basic idea: get all the links in the document, then loop through them adding an onclick event if the REL attrib matches poppinRel, or if the URL is not within one of the domains specified in myBaseUrl. If we add the event, and it doesn’t have a REL attrib, then add the poppinRel.

PopWin(e)

function PopWin(e) {
  if (window.event && window.event.srcElement) a = window.event.srcElement
  else if (e && e.target) a = e.target
  if (!a) return;
  a = getParent(a,’a');
  // Open a new window with the link’s href.
  var newwin = window.open(a.href);
  // The thought is that if the new window didn’t (popups blocked or whatever)
  // we want to return true so the link is follow normally.
  if( newwin )
  {
    if( e.returnValue ) e.returnValue = false;
    if( e.preventDefault ) e.preventDefault();
    return false;
  }
  else return true;
}
function getParent(el, pTagName)
{
  if (el == null) return null;
  else if(el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) return el;
  else return getParent(el.parentNode, pTagName);
}

This is the real workhorse of the function - the event listener. The first three lines try as hard as they can to figure out which link you clicked on. If it can’t, it goes home, and you end up with a regular ol’ same-window link. Then, open a new window with the A’s HREF attrib as the location. If this succeeds, it returns a boolean true, and we save the return value.

If that happens (i.e., the newwin object doesn’t get set to null,) then we don’t want the link to function normally. Otherwise, you end up with the link loading in two windows, which is silly and stupid. The conventional wisdom on the case is that returning false will prevent the link from loading. That was the case in MSIE, but for some reason, Firefox 0.8 insists on double-loading the link in spite of the false return value from the onClick event. Thankfully, in his typically completist way, PPK also pointed out the two possible means of preventing the default behavior, and by sniffing out for them, we can make Firefox behave quite nicely.

Drop the quarter, turn the crank

addEvent(window,'load',makeEmPoppin);
Now that we’ve told it how to do it, just attach the event to the window object. All there is to it!

The astute will notice that addEvent is not standard JavaScript, but rather a function that’s included in this file. It’s pretty basic, and I reuse it a lot. It’s documented better elsewhere, and only slightly modified for this example.

Ok, so give it to me, already!

Here you go. Note: if you set myBaseUrl to something invalid, like ‘&&’, then it will pop up for all links. Also note that that is annoying, and defeats the purpose of this whole doohickey.

11 Responses to “Putting Links in New Windows”

  1. On November 22nd, 2004 at 15:58:49, Isaac Schlueter Said:

    I just added the getParent function to deal with a strange Mozilla bug where sometimes the #text node is the target of the click event, instead of the A.

  2. On May 31st, 2005 at 09:07:40, scott Said:

    Isaac,

    Thanks for the post. I have implemented the script, which is very easy to do.

    Aside from needing to recode the few pop-ups that I have (using either a js function or window.open js in the HTML), it went very smoothly.

    I do have a question.

    Is there a way to insert a link so as to uniquely control the position/size of the ‘newwin’ popUp window? (The few, exsiting popUps I had, defined size/position on a per-case basis and I would like to retain this capability).

    I can’t seem to quickly find a way to do this, using this script and retaining graceful degredation in the event a visitor has JS turned off.

    Thanks.

    -Scott

  3. On May 31st, 2005 at 10:38:38, Schlueterica Said:

    Putting Links in New Windows, part III got an interesting comment from Scott of http://randsco.com regarding my Putting Links in New Windows article.

    Is there a way to insert a link so as to uniquely control the position/size of the ‘newwin’ popUp window?

    Currently, no. However, if…

  4. On July 15th, 2005 at 12:30:26, Justin Haugens Said:

    Hey Isaac, I tried to enter the codes into my blog but I’m not 100% sure where it’s suppose to go. Should it be in the custom.css file in b2evolution?

    Any specific location in there? I am hoping we can get this to work because it will save me a lot of time!!!

  5. On July 15th, 2005 at 14:23:55, Isaac Schlueter Said:

    Justin,

    Check out the test page and view the source.

    The best thing to do is put all the JavaScript code in a separate popup.js file. Save this file in your /skins/skin_name/ folder (where skin_name is the name of the skin that you’re using it with.)

    Then, in /skins/skin_name/_main.php, add something like this right after the <head> tag:
    <script type="text/javascript" src="popup.js"></script>

  6. On July 16th, 2005 at 09:25:47, Justin Said:

    BRAVO!!!!!!!!!!!!!!!!

    THANKS BUDDY!

  7. On October 8th, 2006 at 19:16:11, altoyes Said:

    hi issac
    thankyou for the script.
    i have it working. all external links are opening in a new window.

    i need to be able to add attributes to the window.
    can i do that with your script?

    if so,
    could you give me detail as i am having difficulty understanding the language.

    alto

  8. On October 8th, 2006 at 20:51:31, altoyes Said:

    hi again isaac

    for example:
    might i do this? to the popup windows section of the script?


    function PopWin(e) {
    if (window.event && window.event.srcElement) a = window.event.srcElement
    else if (e && e.target) a = e.target
    if (!a) return;

    a = getParent(a,'a');
    // Open a new window with the link's href.
    var newwin = window.open(a.href);
    // Open a new window with the link’s href.
    var newwin = window.open(a.href)(features = scrollbars,status,resizable,top=50,left=50,height=200,width=300);

    // The thought is that if the new window didn't (popups blocked or whatever)
    // we want to return true so the link is follow normally.
    if( newwin )
    {
    if( e.returnValue ) e.returnValue = false;
    if( e.preventDefault ) e.preventDefault();
    return false;
    }
    else return true;
    }

  9. On October 9th, 2006 at 00:05:39, altoyes Said:

    hi isaac

    this is probably more like it, though i don’t have it working yet.


    function PopWin(e) {
    if (window.event && window.event.srcElement) a = window.event.srcElement
    else if (e && e.target) a = e.target
    if (!a) return;

    a = getParent(a,'a');
    // Open a new window with the link's href.
    var features = "toolbar=no,location=no,directories=no,menubar=no,scrollbars=yes,status=yes,resizable=yes,width=500,height=400";
    var newwin = window.open(a.href, features);

    // The thought is that if the new window didn't (popups blocked or whatever)
    // we want to return true so the link is follow normally.
    if( newwin )
    {
    if( e.returnValue ) e.returnValue = false;
    if( e.preventDefault ) e.preventDefault();
    return false;
    }
    else return true;
    }

  10. On October 9th, 2006 at 00:23:08, altoyes Said:

    hi isaac:
    i am using your script as is as my wrong alterations to your script mess up my other popup windows - the poplinks are all on the right right sidebar
    http://parentsawake.com/

    the popup script is from http://incsub.org/feed2js/index.php?s=build

    yet i would really love to get your windows to open as my other popup windows.

    alto

  11. On October 9th, 2006 at 11:01:58, Isaac Said:

    Alto:

    If you need to control the attributes of the popup windows, then please see this page:
    http://isaacschlueter.com/2005/05/new_window_links_2/

Leave a Reply

Comments are moderated like crazy using a variety of plugins. There is a very high likelihood that your comment won't show up right away, especially if you have never commented here before, but it was not deleted.

Please be patient, and do not post your comment more than once. It will show up once it is approved.

You must be logged in to post a comment.