Making external links open in a new window

I wanted that by default all external links on my page open in a new window (or tab), but I wanted to have an option to force specific links to open in same window. I also wanted to keep XHTML 1.0 Strict compliancy ... so "target" attribute in the anchor tags was not an option.

In the end I've come up with the following javascript code that I've put into a separate *.js file and include in all pages (in page.tpl.php of my Drupal template):
function m_addLoadEvent(func) {
  var oldOnload = window.onload;
  if (typeof window.onload != "function") {
    window.onload = func;
  }
  else {
    window.onload = function() {
      oldOnload();
      func();
    }
  }
}

function m_externalLinks() {
  if (typeof document.getElementsByTagName == "undefined") {
    return;
  }
  var anchors = document.getElementsByTagName("a");
  var mesg = "(this link opens in a new window)";
  for (var i = 0; i < anchors.length; i++) {
    var anchor = anchors[i];
    var rel = "" + anchor.getAttribute("rel");
    if (
      typeof anchor.hostname != "undefined"
      && typeof anchor.protocol != "undefined"
      && anchor.protocol.indexOf("javascript") < 0
      && (
        (
          rel.length > 0
          && rel.indexOf("blank") >= 0
        )
        || (
          (
            rel.length == 0
            || rel.indexOf("self") < 0
          )
          && anchor.hostname != document.location.hostname
        )
      )
    ) {
      anchor.target = "_blank";
      if (typeof anchor.title != "undefined") {
        anchor.title = (anchor.title ? anchor.title + " " : "") + mesg;
      }
      else {
        anchor.title = mesg;
      }
    }
  }
}
m_addLoadEvent(m_externalLinks);

I copied Drupal's "addLoadEvent" into my script since the Drupal version of the function is not loaded in all generated pages.

"m_externalLinks" does the following:
  1. changes "target" attribute of anchors to "_blank" if the anchor points to an url with a hostname different from the current site's hostname
  2. also changes the title of the anchor to "<original title> (this link opens in a new window)" in the above described case
  3. you can force internal links to open in a new window by adding the "rel=blank" attribute to the anchor
  4. you can force external links to open in same window by adding the "rel=self" attribute to the anchor
This works perfectly for me and no additional module or whatsoever is required.

Update: meanwhile a module named Extlink was created which does just what I intended with the above code. You might consider using the module instead of this code snippet (I use the module since it's a more "Drupalish" way of doing it Smile ).

PS: I've found a bug in Firefox which raises an exception (and thus prevents the script from processing all links on the page) if there's a link without a href attribute. You can find details on the bug here.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Hi there, can you help me a

Hi there, can you help me a bit with this?

It works great if i paste it into the head of my page.tpl.php - but how do I include it? I tried a php include but that didnt work...

Thanks!

ok i got round this by

ok i got round this by putting the JS in a seperate php file and then php include that file in the head of page.tpl.php

do not use PHP's include ...

Using PHP's include() for having an external JavaScript sourced into your page is an overkill for this task. This way the php engine has to dynamically include a static (!) page in the php that calls include().

I did it like this:
  1. I've put the JavaScript code into a file called "commons.js". I've put this file into a subdir in "www", eg. "www/files/website/commons.js".
  2. I've added a <script> tag with an "src" attribute to the <head> part of "page.tpl.php" to include the "commons.js" file. The <head> looks now something like this:
    <head>
      <title><?php print $head_title ?></title>
      <meta http-equiv="Content-Style-Type" content="text/css" />
      <?php print $head ?>
      <script type="text/javascript" src="/files/website/common.js"></script>
      <?php print $styles ?>
    </head>
PS: actually the Drupal-way of including a JS-file in your theme would be a call to the PHP function drupal_add_js(), but I found that it did not always work for me. So I just put the <SCRIPT> tag into page.tpl.php directly.

translating the new title of the links

On a multilingual site you could put the JS-code into a php file (eg. "www/themes/mytheme/common-js.inc.php") and exchange the following line:
  anchor.title = (anchor.title ? anchor.title + " " : "") + "(this link opens in a new window)";
with something like this:
  anchor.title = (anchor.title ? anchor.title + " " : "") + "(<?= t("this link opens in a new window") ?>)";

... and refer to this PHP file in the <head> part of your "page.tpl.php" to get the JS included:
<head>
  <title><?php print $head_title ?></title>
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <?php print $head ?>
  <script type="text/javascript" src="common-js.inc.php"></script>
  <?php print $styles ?>
</head>

This way you can add translation of the "this link opens in a new window" text via Drupal's "administration / localization" page. Smile

thanks for the tips - i will

thanks for the tips - i will give it a try.

Having a little problem now - im using the quicktags bar and whenver i hit a quicktag button its opening a new window. This is pretty annoying - i cant see why its happening though as there are no external links referenced anyway in the quicktag files.

It would also be cool if one could enter other domains in the script which didnt want to open in a new window - eg my site is on both a .org and a .org.uk address!

Thanks for your help Smile

replace the generic hostname comparison with a specific one

You could replace the && anchor.hostname != document.location.hostname comparison with something that matches all your domain names.

Eg. in your case I'd add something like this:
&& anchor.hostname != "domain.org"
&& anchor.hostname != "domain.org.uk"

This is not a generic solution (since the code becomes site-specific due to the included domain names), but it's simple and works. Smile

Very good post, thanks a lot.

Very good post, thanks a lot.

So.. What about javascript

So.. What about javascript links etc. how will "javascript:mceToggle('edit-teaser', 'wysiwyg4teaser');" this work ?

added support for JS links

You're right! I did not test it with JavaScript links and I just see that IE fills the "hostname" property of JavaScript links with the JavaScript code that you put behind the bogus "javascript:" protocol identifier. I'm not really surprised ... IE is full of such interesting "features". Sad

I added support for javascript links which means you can define by including or excluding a line of code whether to open javascript links on current page or on a new page by default.

How to define the size of the new window

Hi, this works great for me; but, how could I define the parameters of the new window: size, scroll etc...

you cannot

You cannot or at least not as it is now. My script only changes the target attribute of <A> anchor tags (and the title as well, but that's just eye candy) to make the links open in a new window. One can define window attributes (size, scrollbar, etc.) if and only if you use a JavaScript function call to open the new window.

My script could be modified to replace the href attribute with some JavaScript function call to open the window in a controlled way. First add a new function to the script for opening a new window, eg.:
function openWindow(url) {
  window.open(url, "_blank", "width=600,height=400,menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");
}

Then replace the anchor.target = "_blank"; line in the m_externalLinks function with something like this:anchor.href = "javascript:openWindow('" + anchor.href + "')";

I don't know whether the above example really works (haven't tested it), but you see the "trick" and can debug it yourself.

You can check the Mozilla Developer Center for a full list of available parameters for use with the window.open function.

Thank you

Thank you very much,

It works nicely! I think it could be more useful than only open a new window, once you can preserve the visibility of your original site, while give a view of a external one.

Again,

Thank you

Open single internal link in new window

Is it possible to open only one internal link in new window?
How should I do it?

Please advise
Thank you

Re: Open single internal link in new window

Did you read my post carefully? Shock Using my code internal links are not opened automatically in a new window/tab, but only if explicitly specified with the rel="blank" attribute (in the anchor tag).

Use Extlink for Drupal sites

Btw. in the mean time (in March 2007) a module named Extlink was created for Drupal that does just what i intended. So if you're using Drupal, you might want to look into that module instead using my code (at least I've switched to using Extlink).

New window in primary link

müzso,
Thanks for the reply,and I'm sorry for not being clear,
Im using extlink, but I need to configure the one primary link which link to Mantis in the same domain.
The extlink can open all links(internal and external) in new window,but I only need one particular link.
Do you have any idea?

Thank you very much

Re: New window in primary link

Unfortunately Extlink (at least the 5.x-1.6 version) does not provide any means to specify (force) on a link-by-link basis whether to open in a new window or not. You've to modify extlink.js for that.
Replace the following code in extlink.js:
    $("a").each(function(el) {
      try {
        var url = this.href.toLowerCase();
        if (url.indexOf('http') == 0 && !url.match(internal_link)) {
          external_links.push(this);
        }
        else if (url.indexOf('mailto:') == 0) {
          mailto_links.push(this);
        }
      }

with something like this:
    $("a").each(function(el) {
      try {
        var url = this.href.toLowerCase();
        if ((url.indexOf('http') == 0 && !url.match(internal_link)) || $(this).hasClass("ext")) {
          external_links.push(this);
        }
        else if (url.indexOf('mailto:') == 0) {
          mailto_links.push(this);
        }
      }

Now you should be able to force any single link to open in a new window by adding the "ext" class to the anchor tag.

PS: I didn't test the above code so it might have errors in it (but better not since I only touched one line Smile ). With a little modification you could add the feature to force external links to open in the current window too.

Re: New window in primary link

müzso,
Thank you so much for your help,
sorry for another dumb question.
How should I add the "ext" class to the anchor tag?
Should I just type this in primary menu path:
http://localhost/mantis ext

Thank you so much.

Re: New window in primary link

No. You did not mention previously that you'd like to have primary links to open in a new window. For Drupal primary links you cannot do it that easily. I was talking about custom links created with eg. raw HTML (like <A HREF="some/relative/link" CLASS="ext">this is a link</A>). Primary links are a feature of Drupal ... you can specify them on a fixed UI and customize their look-and-feel through CSS. However you cannot assign any specific class to a primary menu link through the admin screen in Drupal. You could do that by using some JavaScript (eg. jQuery).

Create a new block (in Administer/Site building/Blocks), place it in the footer section, use input format "Full HTML", and place the following code into it:
<script type="text/javascript">
  $("ul.primary-links > li.menu-1-1-2 a").addClass("ext");
</script>

The specific menu-1-1-2 class should specify the menuitem of your choice.

PS: the above jQuery line is not tested ... might not work at all or might need some debugging. It's just one way to do what you want.

Re: New window in primary link

müzso,
Thank you for the explanation,
and thank you for your patience.
I will give it a try.

Thank you very much

Re: New window in primary link

müzso,
Thanks for the advise, its working.
But after that I upgraded to Drupal 6.6 and unload all my modules,
testing to replace the text editor,
and suddenly the <code>$("ul.primary-links > li.menu-1-1-2").addClass("ext");</code> start showing in the footer, and the "magic" is missing Smile.
Is there any other way to put the snippet?
Im still trying to figure it out where did I went wrong in the input filter Smile.

Thank you so much for your help.

Re: New window in primary link


Thanks for the advise, its working.
Actually it's strange that it was working for you. There was a small error in the example code. Originally I wrote this:
$("ul.primary-links > li.menu-1-1-2").addClass("ext");

But this could not work since it added the "ext" class to the <li> tag and not the <a> tag. I've fixed that in the comment with the example.

I don't know what broke, when you upgraded to Drupal 6.6. What Drupal version did you upgrade from? 5.x? In Drupal 6.x jQuery got a major update, but actually my example code should be compatible with the jQuery both in Drupal 5.x and 6.x. Is the input format of the given block still "Full HTML"? Did you put the <script ...> ... </script> tags around the jQuery code?

Thanks for the guide!

Thanks for the great guide. I am new to Drupal and trying to learn more so any help is a good addition to the knowledge.This surely helps.Your efforts are highly appreciated.Thanks for posting.
-James Keller

Thanks!

thanks a trillion for that code!!!
PERFECT Solution to what i wanted!!!!

Its a grate article it helps

Its a grate article it helps us a lot, Thanks for sharing.

very interesting, good job!

very interesting, good job!