Test if links are external with jQuery / javascript?

JavascriptJqueryLocationHref

Javascript Problem Overview


How do I test to see if links are external or internal? Please note:

  1. I cannot hard code the local domain.
  2. I cannot test for "http". I could just as easily be linking to my own site with an http absolute link.
  3. I want to use jQuery / javascript, not css.

I suspect the answer lies somewhere in location.href, but the solution evades me.

Thanks!

Javascript Solutions


Solution 1 - Javascript

I know this post is old but it still shows at the top of results so I wanted to offer another approach. I see all the regex checks on an anchor element, but why not just use window.location.host and check against the element's host property?

function link_is_external(link_element) {
    return (link_element.host !== window.location.host);
}

With jQuery:

$('a').each(function() {
    if (link_is_external(this)) {
        // External
    }
});

and with plain javascript:

var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
    if (link_is_external(links[i])) {
        // External
    }
}
    

Solution 2 - Javascript

var comp = new RegExp(location.host);

$('a').each(function(){
   if(comp.test($(this).attr('href'))){
       // a link that contains the current host           
       $(this).addClass('local');
   }
   else{
       // a link that does not contain the current host
       $(this).addClass('external');
   }
});

Note: this is just a quick & dirty example. It would match all href="#anchor" links as external too. It might be improved by doing some extra RegExp checking.


Update 2016-11-17

This question still got a lot of traffic and I was told by a ton of people that this accepted solution will fail on several occasions. As I stated, this was a very quick and dirty answer to show the principal way how to solve this problem. A more sophisticated solution is to use the properties which are accessible on a <a> (anchor) element. Like @Daved already pointed out in this answer, the key is to compare the hostname with the current window.location.hostname. I would prefer to compare the hostname properties, because they never include the port which is included to the host property if it differs from 80.

So here we go:

$( 'a' ).each(function() {
  if( location.hostname === this.hostname || !this.hostname.length ) {
      $(this).addClass('local');
  } else {
      $(this).addClass('external');
  }
});

State of the art:

Array.from( document.querySelectorAll( 'a' ) ).forEach( a => {
    a.classList.add( location.hostname === a.hostname || !a.hostname.length ? 'local' : 'external' );
});

Solution 3 - Javascript

And the no-jQuery way

var nodes = document.getElementsByTagName("a"), i = nodes.length;
var regExp = new RegExp("//" + location.host + "($|/)");
while(i--){
    var href = nodes[i].href;
    var isLocal = (href.substring(0,4) === "http") ? regExp.test(href) : true;
    alert(href + " is " + (isLocal ? "local" : "not local"));
}

All hrefs not beginning with http (http://, https://) are automatically treated as local

Solution 4 - Javascript

var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');

Usage:

external.test('some url'); // => true or false

Solution 5 - Javascript

Here's a jQuery selector for only external links:

$('a[href^="(http:|https:)?//"])') 

A jQuery selector only for internal links (not including hash links within the same page) needs to be a bit more complicated:

$('a:not([href^="(http:|https:)?//"],[href^="#"],[href^="mailto:"])')

Additional filters can be placed inside the :not() condition and separated by additional commas as needed.

http://jsfiddle.net/mblase75/Pavg2/


Alternatively, we can filter internal links using the vanilla JavaScript href property, which is always an absolute URL:

$('a').filter( function(i,el) {
    return el.href.indexOf(location.protocol+'//'+location.hostname)===0;
})

http://jsfiddle.net/mblase75/7z6EV/

Solution 6 - Javascript

You forgot one, what if you use a relative path.

forexample: /test

		hostname = new RegExp(location.host);
	        // Act on each link
	        $('a').each(function(){

            // Store current link's url
            var url = $(this).attr("href");

            // Test if current host (domain) is in it
            if(hostname.test(url)){
               // If it's local...
               $(this).addClass('local');
            }
            else if(url.slice(0, 1) == "/"){
            	$(this).addClass('local'); 
            }
            else if(url.slice(0, 1) == "#"){
                // It's an anchor link
                $(this).addClass('anchor'); 
            }
            else {
               // a link that does not contain the current host
               $(this).addClass('external');                        
            }
        });

There are also the issue of file downloads .zip (local en external) which could use the classes "local download" or "external download". But didn't found a solution for it yet.

Solution 7 - Javascript

const isExternalLink = (url) => {
    const tmp = document.createElement('a');
    tmp.href = url;
    return tmp.host !== window.location.host;
};

// output: true
console.log(isExternalLink('https://foobar.com'));
console.log(isExternalLink('//foobar.com'));

// output: false
console.log(isExternalLink('https://www.stackoverflow.com'));
console.log(isExternalLink('//www.stackoverflow.com'));
console.log(isExternalLink('/foobar'));
console.log(isExternalLink('#foobar'));

The benefit of using this approach is that:

  • It would automatically resolve the hostname for relative paths and fragments;
  • It works with protocol-relative URLs

To demonstrate this, let's look at the following examples:

const lnk = document.createElement('a');
lnk.href = '/foobar';

console.log(lnk.host); // output: 'www.stackoverflow.com'
const lnk = document.createElement('a');
lnk.href = '#foobar';

console.log(lnk.host); // output: 'www.stackoverflow.com'
const lnk = document.createElement('a');
lnk.href = '//www.stackoverflow.com';

console.log(lnk.host); // output: 'www.stackoverflow.com'

Solution 8 - Javascript

With jQuery

jQuery('a').each(function() {
    if (this.host !== window.location.host) {
        console.log(jQuery(this).attr('href'));
    }
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Solution 9 - Javascript

You can use is-url-external module.

var isExternal = require('is-url-external');
isExternal('http://stackoverflow.com/questions/2910946'); // true | false 

Solution 10 - Javascript

/**
     * All DOM url
     * [links description]
     * @type {[type]}
     */
    var links = document.querySelectorAll('a');
    /**
     * Home Page Url
     * [HomeUrl description]
     * @type {[type]}
     */
    var HomeUrl = 'https://stackoverflow.com/'; // Current Page url by-> window.location.href

    links.forEach(function(link) {
        link.addEventListener('click', function(e) {
            e.preventDefault();

            // Make lowercase of urls
            var url = link.href.toLowerCase();
            var isExternalLink = !url.includes(HomeUrl);

            // Check if external or internal
            if (isExternalLink) {
                if (confirm('it\'s an external link. Are you sure to go?')) {
                    window.location = link.href;
                }
            } else {
                window.location = link.href;
            }
        })
    })

<a href="https://stackoverflow.com/users/3705299/king-rayhan">Internal Link</a>
<a href="https://wordpress.stackexchange.com/">External Link</a>

Solution 11 - Javascript

This should work for any kind of link on every browser except IE.

// check if link points outside of app - not working in IE
                try {
                    const href = $linkElement.attr('href'),
                        link = new URL(href, window.location);

                    if (window.location.host === link.host) {
                        // same app
                    } else {
                        // points outside
                    }
                } catch (e) { // in case IE happens}

Solution 12 - Javascript

Solution 13 - Javascript

For those interested, I did a ternary version of the if block with a check to see what classes the element has and what class gets attached.

$(document).ready(function () {
    $("a").click(function (e) {

        var hostname = new RegExp(location.host);
        var url = $(this).attr("href");
   
        hostname.test(url) ?
        $(this).addClass('local') :
        url.slice(0, 1) == "/" && url.slice(-1) == "/" ?
        $(this).addClass('localpage') :
        url.slice(0, 1) == "#" ?
        $(this).addClass('anchor') :
        $(this).addClass('external');

        var classes = $(this).attr("class");

        console.log("Link classes: " + classes);

        $(this).hasClass("external") ? googleAnalytics(url) :
        $(this).hasClass("anchor") ? console.log("Handle anchor") : console.log("Handle local");

    });
});

The google analytics bit can be ignored but this is where you'd probably like to do something with the url now that you know what type of link it is. Just add code inside the ternary block. If you only want to check 1 type of link then replace the ternaries with an if statement instead.

Edited to add in an issue I came across. Some of my hrefs were "/Courses/" like so. I did a check for a localpage which checks if there is a slash at the start and end of the href. Although just checking for a '/' at the start is probably sufficient.

Solution 14 - Javascript

I use this function for jQuery:

$.fn.isExternal = function() {
  var host = window.location.host;
  var link = $('<a>', {
    href: this.attr('href')
  })[0].hostname;
  return (link !== host);
};

Usage is: $('a').isExternal();

Example: https://codepen.io/allurewebsolutions/pen/ygJPgV

Solution 15 - Javascript

This doesn't exactly meet the "cannot hardcode my domain" prerequisite of the question, but I found this post searching for a similar solution, and in my case I could hard code my url. My concern was alerting users that they are leaving the site, but not if they are staying on site, including subdomains (example: blog.mysite.com, which would fail in most of these other answers). So here is my solution, which takes some bits from the top voted answers above:

Array.from( document.querySelectorAll( 'a' ) ).forEach( a => {
  a.classList.add( a.hostname.includes("mywebsite.com") ? 'local' : 'external' );
});

$("a").on("click", function(event) {
  if ($(this).hasClass('local')) {
    return;
  } else if ($(this).hasClass('external')) {
    if (!confirm("You are about leave the <My Website> website.")) {
      event.preventDefault();
    }
  }
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatrymView Question on Stackoverflow
Solution 1 - JavascriptDavedView Answer on Stackoverflow
Solution 2 - JavascriptjAndyView Answer on Stackoverflow
Solution 3 - JavascriptSean KinseyView Answer on Stackoverflow
Solution 4 - JavascriptJamesView Answer on Stackoverflow
Solution 5 - JavascriptBlazemongerView Answer on Stackoverflow
Solution 6 - JavascriptFreelance webdesigner NLView Answer on Stackoverflow
Solution 7 - JavascriptSaeed ShahbaziView Answer on Stackoverflow
Solution 8 - JavascriptSajjad MoghayyadView Answer on Stackoverflow
Solution 9 - JavascriptmrdedView Answer on Stackoverflow
Solution 10 - JavascriptKing RayhanView Answer on Stackoverflow
Solution 11 - JavascriptfuuchiView Answer on Stackoverflow
Solution 12 - JavascriptSavagemanView Answer on Stackoverflow
Solution 13 - JavascriptGeorge F DelaneyView Answer on Stackoverflow
Solution 14 - JavascriptAllure Web SolutionsView Answer on Stackoverflow
Solution 15 - JavascriptJacob HixonView Answer on Stackoverflow