Test if links are external with jQuery / javascript?
Solution 1
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 2
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 3
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
var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
Usage:
external.test('some url'); // => true or false
Solution 5
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;
})
Matrym
Updated on July 05, 2022Comments
-
Matrym almost 2 years
How do I test to see if links are external or internal? Please note:
- I cannot hard code the local domain.
- I cannot test for "http". I could just as easily be linking to my own site with an http absolute link.
- I want to use jQuery / javascript, not css.
I suspect the answer lies somewhere in location.href, but the solution evades me.
Thanks!
-
Matrym about 14 yearsThis works fairly well, although I'm going to hold off on an answer in case someone else has a more elegant solution. Out of curiosity, why do anchor links register as external?
-
Sean Kinsey about 14 yearsI'm pretty sure that this will not work with relative urls.
attr
is supposed to return the attribute, not the property (the property might be resolved, not the attribute). -
Sean Kinsey about 14 yearsjsfiddle.net/zuSeh It is verified that this method does not work for relative urls.
-
Savageman about 14 yearsThis answer is more accurate. Relatives URL are also important.
-
Grimace of Despair almost 12 yearsIf I'm not mistaken, "($|/" should actually be "($|/)" with a closing brace
-
Grimace of Despair almost 12 yearsJust ran into this and want to confirm that this code indeed has a problem with relative urls. So +1 on Sean and Damian.
-
feeela over 11 years+1 for the regexp, though it does not completely solve the question
-
framp about 11 yearsThis is close to the solution but you should also check if the href property does begin with
location.protocol+'//'+location.host
. Check this fiddle: jsfiddle.net/framp/Ag3BT/1 -
1nfiniti about 11 yearsWhy are you doing this with a while loop? Seems to me like it would make more sense to use event delegation via $(document).on('click', 'a', function({}); and test the specific link that was clicked (at the point of being clicked). That way, you don't needlessly loop through all the links on the page, and it will allow for any elements added to the page via ajax after the initial DOM ready... There's actually a point to using jQuery sometimes (beyond being a "fanboy").
-
rossipedia over 10 yearsThis won't work if you use protocol agnostic urls, ie:
href="//somedomain.com/some-path"
-
Adam Plocher about 10 yearsNot all relative URLs start with
/
. You can reference something likeimages/logo.png
which is one folder down from your current location. In that case you're referencing a relative path in your relative URL, it will be a different meaning in different directories on your site./images/logo.png
is an absolute path of whatever site it's running on (hence the relativity). Your code will not include relative paths likeimages/logo.png
. -
Kevin B about 10 yearsIt will work for relative too if you use the href property instead of the href attribute.
-
Gone Coding about 10 years+1: OK there was a way to up-vote you again for this answer :)
-
tremby over 9 yearsThis is the only answer which makes sense to me -- the others are way overengineered. Are there any arguments against this method?
-
Daved over 9 yearsI've seen people still viewing this question and solution so I wanted to link to my proposed solution below which should handle all checks without RegEx or relative issues: stackoverflow.com/a/18660968/2754848
-
mikesir87 over 9 yearsFirst one doesn't work as "//code.jquery.com/jquery.min.js" is a completely legit URL, but not internal. The protocol and colon are not required, as the browser will use whatever the current site is using (a semi-sorta protocol relative URL).
-
Mark Ribau about 9 yearsThe jQuery above works for me in Safari, and handles all of the issues that the others handle -- local anchors, relative URLs, cross-protocol URLs, etc. Note: example.com and example.com will be marked as internal; that may be important to you.
-
dana over 8 yearsThis is the best answer. This makes use of the
a.host
property which is probably unknown to the average JavaScript developer (including myself before reading this). -
kthornbloom about 8 yearsFYI, your external selector gives an error:
Uncaught Error: Syntax error, unrecognized expression: a[href^="(http:|https:)?//"])
-
Nicholas almost 8 yearsPerfect, just what I was looking for! Clean and simple without all the regex hassle which isn't needed.
-
Laogeodritt over 7 yearsjAndy's solution will also fail (false detection of internal link) in the case that an external URL contains the hostname, e.g. external-site.com/domaintools/original.hostname.com/stats or something like that. Overall I favour @Daved's solution as the most elegant and most direct check—far less error prone when the browser has already resolved the URL and you're checking its resolved hostname!
-
jAndy over 7 yearsUpdated answer.
-
Danny Coulombe about 7 yearsWorks flawlessly. Best answer.
-
Andrew about 7 yearsthis will fail when port number is specified, also if "www" is left out
-
Daved about 6 yearsI'm a little late to the follow up here, but I would argue to @Andrew that "www" is an identifier that would indicate a different match. And though the domain might be the same, the host would be different with WWW and I think that's a valid condition. The port can be checked by using a ".port" comparison as well if they both != ''.
-
Mo Ali about 6 years@jAndy I believe that, correct me if I'm wrong, there is no need for using
Array.from
in last part of your answer, instead just wrap the expression in parens to become(document.querySelectorAll( 'a' )).forEach
-
jAndy about 6 years@MoAli At the time I wrote that, most browsers didn't provide the
forEach
method on theHTMLElement prototype
. I still wouldn't dare to directly call that even today to be honest. Of course there are still some versions and mobile browsers which also don't supportArray.from
, but chances are way higher here, than.forEach
being on the prototype. -
Rahul J. Rane almost 5 years@jAndy - Thanks for this solution. one more point is that if the href has relative URL having # in it, then it is not working. may you please give a solution for the same?
-
Vincente over 4 years@jAndy, why do you check "!this.hostname.length". In chrome it's always not empty even if href does not have hostname. Can it be empty in some browser?
-
jAndy over 4 years@Vincente I just did a quick check here on the SO site... If you filter all results from
document.querySelectorAll( 'a' ).forEach( anchor => {});
forif( !anchor.hostname.length)
you get lots of results. For instance anchors which only have aname
orclass
but nohref
. -
Vincente over 4 years@jAndy, thank you. I also tested it in IE11, link with href="/page1" does not have hostname property, unlike in Chrome.
-
giovannipds over 4 years@tremby Yes, I have just one argument against it, it depends on
a
DOM node. Imagine that I can have only thehref
string
, I wouldn't be able to test it without creating a ghost node for it. -
tremby over 4 years@Daved Looking back, maybe testing
.origin
of each would make most sense. That'll include the scheme and port. -
tremby over 4 years@giovannipds, you can use
new URL(myHrefString
) instead of a new DOM node. -
Cezary Tomczyk over 3 yearsNote to
new URL(href, window.location)
: Argument of type 'Location' is not assignable to parameter of type 'string | URL | undefined'. -
Tyler2P over 2 yearsA good answer will always include an explanation why this would solve the issue, so that the OP and any future readers can learn from it.
-
Mahdi Bashirpour over 2 yearsThe description was great. Thankful
-
Vah Run over 2 yearsThis is a worthy answer which could use its own space...