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!
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
}
}
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' );
});
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
var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
Usage:
external.test('some url'); // => true or false
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/
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.
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'
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>
You can use is-url-external module.
var isExternal = require('is-url-external');
isExternal('http://stackoverflow.com/questions/2910946'); // true | false
/**
* 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;
}
})
})
Internal Link
External Link
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}
Yes, I believe you can retrieve the current domain name with location.href. Another possibility is to create a link element, set the src to / and then retrieving the canonical URL (this will retrieve the base URL if you use one, and not necessarily the domain name).
Also see this post: Get the full URI from the href property of a link
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.
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
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();
}
}
});
this works for me:
function strip_scheme( url ) {
return url.replace(/^(?:(?:f|ht)tp(?:s)?\:)?\/\/(www\.)?/g, '');
}
function is_link_external( elem ) {
let domain = strip_scheme( elem.attr('href') );
let host = strip_scheme( window.location.host );
return ! domain.indexOf(host) == 0;
}
Related
I need all blog product pages to show in a popup. In order to show in the popup their url must be in the form https://expample.com?/modal-link=blog_page_url. (I'm using the plugin and this is the requirement)
I would like to write a code in javascript that checks the URL. If the URL of the page contains the word 'product' I would like prepend to the url: https://expample.com?/modal-link= inorder to enable it to be shown in a popup.
I'm using the code below:
if(window.location.href.indexOf("product") > -1) {
var url = window.location.href;
url_new = 'https://example.com/?modal-link=' + url
} else {
}
window.location.href = url_new;
The is creating a new URL but it is causing it to be added an infinite amount of time.
How should I be doing this?
Follow on question: (should I open a new question for this?)
I would like to adapt the code so the page does not reload during the redirect.
I know there are other posts about this eg How do I modify the URL without reloading the page? or https://stackoverflow.com/questions/3338642/updating-address-bar-with-new-url-without-hash-or-reloading-the-pagebut could someone please help me modify my javascript code for this specific case?
Would I need to use the lines below?
document.location.hash = 'afterhash';
history.pushState('data to be passed', 'Title of the page', '/test');
I'm at a loss which part of my code need to go where in the above lines.
Your recursion is missing a stop condition. For example, if "some_product" contains "product" and you prepend anything to it, it will still contain "product", as in "really_some_product", "really_really_some_product", etc. You can see where this is going, infinite recursion.
So, you need to tell it to stop at some point, which is when the new url already starts with what you intend to prepend to the original one.
Following this, since there's a case in which we don't change anything, we should also not redirect.
var url = window.location.href,
prepend_to_url = "https://example.com/?modal-link=",
url_new = false;
if (url.indexOf(prepend_to_url) == 0) {
// url starts with what we prepend
// so do nothing
} else if(url.indexOf("product") > -1) {
url_new = prepend_to_url + url;
}
if (url_new) { // don't redirect unless we've done something above
window.location.href = url_new;
}
A more concise version of the code above could look like this:
var url = window.location.href,
prepend_to_url = "https://example.com/?modal-link=",
url_new = false;
if (url.indexOf(prepend_to_url) == -1 // url doesn't start with what we prepend
&& url.indexOf("product") > -1 // and our condition is met
) {
url_new = prepend_to_url + url;
}
url_new && (window.location.href = url_new); // equivalent to an "if" statement
What you need is to get the query parameter part of the url by using substr with index of ? to the end of the url
var url_new;
if(window.location.href.indexOf("product") > -1) {
var url = window.location.href.substr(window.location.href.indexOf("?") +1, window.location.href.length);
var newValue = 10;
url_new = 'https://example.com/?modal-link=' + newValue + "&" + url
}
console.log(url_new);
You should initilize the url_new and change it for some condition:
let url_new = window.location.href
if(window.location.href.indexOf("product") > -1) {
url_new = 'https://example.com/?modal-link=' + window.location.href;
}
window.location.href = url_new;
I have a script that adds an param to the url when I click the assigned button - next click replaces it with a new param - this work great.
However - now I have three buttons - and I want each button to assign a param to the url - and replacing any params added by any of the other buttons. It also needs to be placed last behind the params that are already there.
so:
(button1) click3: /m4n?ecom-query=imac&seid=etailer-products&viewMode=3?param=grid
(button2) click4: /m4n?ecom-query=imac&seid=etailer-products&viewMode=3?param=list
(button3) click5: /m4n?ecom-query=imac&seid=etailer-products&viewMode=3?param=smalllist
The url before ?param is dynamic and can look different.
$('.click2').on('click', function() {
console.log("Clicked");
var url = window.location.pathname;
var url = window.location.href;
if (url.indexOf('?param=list') > -1) {
url = url.replace("?param=list", "") + '?param=grid'
} else {
url = url.replace("?param=grid", "") + '?param=list'
}
window.location.href = url;
});
How do I do this, I tried to modify my existing script but had no luck.
I think there is a small error in your approach:
All parameters in url should be connected with an &
so now your url should look like that
/m4n?ecom-query=imac&seid=etailer-products&viewMode=3¶m=grid
now if you want to replace old pram, you need to remove the old value also. For that you can use regex as in following code
url = url.replace(/\¶m=.*/,'') + '¶m=list'
So the full code would be:
$('.click2').on('click', function() {
console.log("click2 Clicked");
var url = window.location.href;
url = url.replace(/\¶m=.*/,'') + '¶m=list';
window.location.href = url;
});
Hope it helps
I'm trying to run an ajax function when links are clicked, but I need to exclude links to anchors on the same page so I don't try to re-load in the page content when I'm simply scrolling down to a different part of the same page.
I know I can test if the href include a hash but that's not good enough:
if (href.indexOf("#") === -1)
Because I will have links that go to another page AND scroll to a local anchor. So I need to test if the href points to the current page AND includes a hash. And in that case I would exclude it from the function. But if it points to a different page and includes a hash it should still be included.
How can I achieve this with jQuery?
This is my take, much slimmer :-
$('a[href*=\\#]').on('click', function (event) {
if(this.pathname === window.location.pathname){
// Do something
}
});
You do not need jQuery for this. Just use regex in Javascript.
if(/^#/.test(href)) { // .test() returns a boolean
/* do not run AJAX function */
} else {
/* run the AJAX function */
}
Explanation:
^# is the regex. // is where you wrap your regex in. ^ means at the beginning of the string and # is what you are looking for. .test() is a javascript function that executes the regex on a given string and return a boolean value.
Read up: RegExp.prototype.test() - JavaScript | MDN
Update 1:
In case if the href is not starting with a # but it still points to the same webpage, your problem is reduced to checking if a string is a substring of another string. You can make use of window.location.href and .indexOf() to achieve this:
if(href.indexOf(window.location.href) > -1) {
/* do not run AJAX function */
} else {
/* run the AJAX function */
}
window.location.href returns the URL of the webpage that you are on and href.indexOf(window.location.href) checks if window.location.href is substring of href;
Example: https://www.example.com/page1 is a substring of https://www.example.com/page1#myDiv
Read up:
Window.location - Web APIs | MDN
String.prototype.indexOf() - JavaScript | MDN
Update 2:
Good find by #Tib. My code in update above was not checking if the hostnames are the same. I have fixed it below:
if(<hostnames are the same>) { // make use of window.location.hostname here to get hostname of current webpage
if(href.indexOf(window.location.href) > -1) {
/* do not run AJAX function */
} else {
/* run the AJAX function */
}
} else {
/* do not run AJAX function */
}
Supported in all browsers:
"use strict"; // Start of use strict
$('a').bind('click', function(event) {
if (this.pathname == window.location.pathname &&
this.protocol == window.location.protocol &&
this.host == window.location.host) {
alert('links to same page');
event.preventDefault();
} else {
alert('links to a different page');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Same Page
<br>
Other Page
Far from perfect, but works for me.
Don't forget to use jQuery.
Have fun with it:
jQuery('a').on('click',function (e) {
var current = window.location.href.split('#')[0];
var goto = jQuery(this).attr('href').split('#')[0];
if (current == goto && this.hash) {
e.preventDefault();
var target = this.hash;
var $target = jQuery(target);
if($target.length){
jQuery('html,body').stop().animate({
'scrollTop': $target.offset().top
}, 900, 'swing');
}
}
});
jQuery('a[href^=#]:not([href=#])').on('click',function (e) {
e.preventDefault();
var target = this.hash;
var $target = jQuery(target);
if($target.length){
history.pushState( null, jQuery('#title').html() , target);
jQuery('html,body').stop().animate({
'scrollTop': $target.offset().top }, 900, 'swing');
}
});
jQuery('a[href=#]').on('click',function (e) {
e.preventDefault();
history.pushState( null, jQuery('#title').html() , location.href.replace(location.hash,""));
jQuery('html,body').stop().animate({
'scrollTop': 0
}, 900, 'swing');
});
/**
* Checks if the href belongs to the same page and returns the anchor if so.
*
* #param {String} href
* #returns {Boolean|String}
*/
function getSamePageAnchor (href) {
var link = document.createElement('a');
link.href = href;
/**
* For IE compatibility
* #see https://stackoverflow.com/a/24437713/1776901
*/
var linkCanonical = link.cloneNode(false);
if (
linkCanonical.protocol !== window.location.protocol ||
linkCanonical.host !== window.location.host ||
linkCanonical.pathname !== window.location.pathname ||
linkCanonical.search !== window.location.search
) {
return false;
}
return link.hash;
}
Here you go,
// extract hash from URL
const hash = new URL(`https://example.com/#your-anchor`).hash; // return => #your-anchor
// if hash exists in the URL, and the anchor is also exists
if(hash.length && document.querySelectorAll(hash).length){
// do something
}
I have a link where I want to get the top level domain name of the domain clicked.
In my example I have
click on this link
When I click on it I want an alert saying
www.example.com
I know window.location.host does this for the actual location of the site but am not sure how to get this for the individual href
I have tried the below but it returns
jQuery(document).on("click", "a", function (event) {
event.preventDefault();
var id = jQuery(this).attr('href') || 'nohref';
alert(window.location.host);
alert(id);
});
http://www.example.com/this/that
rather than
www.example.com
What is the best way to achieve this?
It's pretty straightforward actually, no need for regular expression:
jQuery(document).on("click", "a[href]", function (event) {
event.preventDefault();
alert(this.hostname);
});
See MDN - HTMLAnchorElement.
I'm not sure if it works in Internet Explorer though.
Try
jQuery(document).on("click", "a", function (event) {
event.preventDefault();
var id = jQuery(this).attr('href') || 'nohref';
var domain = id.match(/http[s]?\:\/\/(.*?)[\/$]/)[1]
alert(domain);
});
var domain = jQuery(this).attr('href').match(/^https?:\/\/([^\/]+)/);
if (domain != null && typeof domain[1]!='undefined')
domain = domain[1];
else
domain = '';
You could take a stab at using document.domain, although it's not as widely supported as window.location.host.
I am currently having an issue with IE8/7 (I do not have a choice, I have to add support for these before anyone moans) where IFrames with youtube videos are causing an issue with adding extra URLs into the history so when hitting back I am having to do it 2-3 times before actually getting to go back, my current solution seems to work in newer browsers but not the two I am having an issue with, current solution is:
<script type="text/javascript">
$(document).ready(function () {
$("iframe").each(function () {
var ifr_source = $(this).attr('src');
if (ifr_source.indexOf("youtube") != -1) {
var parent = $(this).parent();
var ifr = $(this).detach();
var wmode = "wmode=transparent";
if (ifr_source.indexOf('?') != -1) {
var getQString = ifr_source.split('?');
var oldString = getQString[1];
var newString = getQString[0];
$(this).attr('src', newString + '?' + wmode + '&' + oldString);
}
else $(this).attr('src', ifr_source + '?' + wmode);
ifr.appendTo($(parent));
}
});
});
Every time you change the iFrame's src attribute, the iFrame registers a new entry in the window's history.
There is a way to prevent this. Instead of using .attr('src'... use:
$("iframe").each(function () {
(this.contentWindow || this.documentWindow).location.replace('your new url');
});
Hope this helps!
just answering my own question here...
Simply adding this into the mix
var frame.src = 'javascript:parent.getFrameHTML()'
Has resolved the issue!