检测foo='http://john.doe'
是否为外部url(与window.location.href
相比)的最快方法是什么?
答案 0 :(得分:29)
如果您认为URL是外部的,如果方案,主机或端口不同,您可以这样做:
function isExternal(url) {
var match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol) return true;
if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(":("+{"http:":80,"https:":443}[location.protocol]+")?$"), "") !== location.host) return true;
return false;
}
答案 1 :(得分:17)
我知道正则表达式版本已被接受,但我敢打赌这比执行正则表达式复杂“更快”。 String.replace
非常快。
var isExternal = function(url) {
var domain = function(url) {
return url.replace('http://','').replace('https://','').split('/')[0];
};
return domain(location.href) !== domain(url);
}
我决定对此进行更多研究,并找到了一种使用正则表达式的更快的方法。
var isExternalRegexClosure = (function(){
var domainRe = /https?:\/\/((?:[\w\d-]+\.)+[\w\d]{2,})/i;
return function(url) {
function domain(url) {
return domainRe.exec(url)[1];
}
return domain(location.href) !== domain(url);
}
})();
在IE中,这比String.replace
方法略快。但是在Chrome和Firefox中它的速度大约是其两倍。此外,在封闭内部仅定义一次Regex而不是仅仅在函数内部通常在Firefox中快30%。
Here is a jsperf检查了确定外部主机名的四种不同方法。
重要的是要注意我试过的每种方法都需要不到1毫秒才能在旧手机上运行。因此,除非您正在进行大批量处理,否则性能可能不应该是您的主要考虑因素。
答案 2 :(得分:16)
我一直在使用psuedosavant的方法,但遇到了一些触发误报的情况,例如无域链接(/about
,image.jpg
)和锚点链接(#about
)。旧方法也会为不同的协议(http
与https
)提供不准确的结果。
这是我稍微修改过的版本:
var checkDomain = function(url) {
if ( url.indexOf('//') === 0 ) { url = location.protocol + url; }
return url.toLowerCase().replace(/([a-z])?:\/\//,'$1').split('/')[0];
};
var isExternal = function(url) {
return ( ( url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url) );
};
以下是一些更新功能的测试:
isExternal('http://google.com'); // true
isExternal('https://google.com'); // true
isExternal('//google.com'); // true (no protocol)
isExternal('mailto:mail@example.com'); // true
isExternal('http://samedomain.com:8080/port'); // true (same domain, different port)
isExternal('https://samedomain.com/secure'); // true (same domain, https)
isExternal('http://samedomain.com/about'); // false (same domain, different page)
isExternal('HTTP://SAMEDOMAIN.COM/about'); // false (same domain, but different casing)
isExternal('//samedomain.com/about'); // false (same domain, no protocol)
isExternal('/about'); // false
isExternal('image.jpg'); // false
isExternal('#anchor'); // false
根据一些基本的jsperf tests,总的来说它更准确,甚至可能会稍快一些。如果您不使用.toLowerCase()
进行不区分大小写的测试,则可以加快速度。
答案 3 :(得分:4)
pseudosavant的回答并不适合我,所以我改进了它。
var isExternal = function(url) {
return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]);
}
答案 4 :(得分:1)
我必须建立在假性和Jon的答案之上,因为我还需要捕获以" //"开头的网址案例。和不包含子域的URL。这对我有用:
Q = quad(@(x) F(x,y), 0, 2)

var getDomainName = function(domain) {
var parts = domain.split('.').reverse();
var cnt = parts.length;
if (cnt >= 3) {
// see if the second level domain is a common SLD.
if (parts[1].match(/^(com|edu|gov|net|mil|org|nom|co|name|info|biz)$/i)) {
return parts[2] + '.' + parts[1] + '.' + parts[0];
}
}
return parts[1]+'.'+parts[0];
};
var isExternalUrl = function(url) {
var curLocationUrl = getDomainName(location.href.replace("http://", "").replace("https://", "").replace("//", "").split("/")[0].toLowerCase());
var destinationUrl = getDomainName(url.replace("http://", "").replace("https://", "").replace("//", "").split("/")[0].toLowerCase());
return !(curLocationUrl === destinationUrl)
};
$(document).delegate('a', 'click', function() {
var aHrefTarget = $(this).attr('target');
if(typeof aHrefTarget === 'undefined')
return;
if(aHrefTarget !== '_blank')
return; // not an external link
var aHrefUrl = $(this).attr('href');
if(aHrefUrl.substr(0,2) !== '//' && (aHrefUrl.substr(0,1) == '/' || aHrefUrl.substr(0,1) == '#'))
return; // this is a relative link or anchor link
if(isExternalUrl(aHrefUrl))
alert('clicked external link');
});

答案 5 :(得分:1)
出于我的目的,我只是对shshaw的答案进行了一些修改,以验证链接是否为空或只是一个字符(假设它是#'#'),原始答案方法返回误报。这是出于我的目的,通过添加一些FA图标向用户表明他们将离开我的页面。
// same thing here, no edit
function checkDomain(url) {
if ( url.indexOf('//') === 0 ) { url = location.protocol + url; }
return url.toLowerCase().replace(/([a-z])?:\/\//,'$1').split('/')[0];
};
function isExternal(url) {
// verify if link is empty or just 1 char + original answer
return (url.length > 1 && url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url);
};
// add some icon to external links (function is called in an init method)
function addExternalLinkIcon(){
$("a[href]").each(function(i,ob){
// we check it
if(isExternal($(ob).attr("href"))){
// then add some beauty if it's external
// (we assume Font Awesome CSS and font is loaded for my example, of course :-P)
$(ob).append(" <i class='fa fa-external-link'></i> ");
}
});
}
答案 6 :(得分:0)
不应该
function is_external( url ) {
return url.match( /[a-zA-Z0-9]*:\/\/[^\s]*/g ) != null;
}
诀窍?不适用于绝对(内部)网址。
答案 7 :(得分:0)
主要问题是如何解析URL,并获取我们的主机名。 可以通过以下方式完成:
var _getHostname = function(url) {
var parser = document.createElement('a');
parser.href = url;
return parser.hostname;
}
var isExternal = (_getHostname(window.location.href) !== _getHostname('http://john.doe'));
或者您可以使用is-url-external模块。
var isExternal = require('is-url-external');
isExternal('http://john.doe'); // true | false
答案 8 :(得分:-1)
您可以简单地使用使用npm软件包is-internal-link
安装
npm install --save is-internal-link
用法
import { isInternalLink } from "is-internal-link"
isInternalLink('https://www.google.com') // false
isInternalLink('/page1') // true
我通常也会这样反应
import React from 'react'
import { Link as ReactRouterLink} from 'react-router-dom'
import { isInternalLink } from 'is-internal-link'
const Link = ({ children, to, activeClassName, ...other }) => {
if (isInternalLink(to)) {
return (
<ReactRouterLink to={to} activeClassName={activeClassName} {...other}>
{children}
</ReactRouterLink>
)
}
return (
<a href={to} target="_blank" {...other}>
{children}
</a>
)
}
export default Link
免责声明:我是这个库的作者