Javascript 检测外部 URL 的最快方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6238351/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Fastest way to detect external URLs
提问by mate64
采纳答案by pseudosavant
I know the regex version has already been accepted but I would bet this is "faster" than doing that complex of a regex. String.replace
is quite fast.
我知道正则表达式版本已经被接受,但我敢打赌这比做复杂的正则表达式“更快”。String.replace
相当快。
var isExternal = function(url) {
var domain = function(url) {
return url.replace('http://','').replace('https://','').split('/')[0];
};
return domain(location.href) !== domain(url);
}
Update
更新
I decided to do a little more research on this and found a faster method that uses a Regex.
我决定对此进行更多研究,并找到了一种使用正则表达式的更快方法。
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);
}
})();
In IE this is slightly faster than the String.replace
method. However in Chrome and Firefox it is about twice as fast. Also, defining the Regex only once inside the closure instead of just inside the function normally is about 30% faster in Firefox.
在 IE 中,这比String.replace
方法稍快。然而,在 Chrome 和 Firefox 中,它的速度大约是其两倍。此外,在 Firefox 中,只在闭包内而不是在函数内定义一次正则表达式大约快 30%。
Here is a jsperfexamining four different ways of determining an external hostname.
这是一个 jsperf检查确定外部主机名的四种不同方法。
It is important to note that every method I've tried takes less than 1msto run even on an old phone. So performance probably shouldn't be your primary consideration unless you are doing some large batch processing.
重要的是要注意,即使在旧手机上,我尝试过的每种方法也只需不到 1 毫秒即可运行。因此,除非您正在进行一些大型批处理,否则性能可能不应该是您的主要考虑因素。
回答by Gumbo
If you consider a URL being external if either the scheme, host or port is different, you could do something like this:
如果您认为 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;
}
回答by shshaw
I've been using psuedosavant's method, but ran into a few cases where it triggered false positives, such as domain-less links ( /about
, image.jpg
) and anchor links ( #about
). The old method would also give inaccurate results for different protocols ( http
vs https
).
我一直在使用 psuedosavant 的方法,但遇到了一些触发误报的情况,例如无域链接 ( /about
, image.jpg
) 和锚链接 ( #about
)。对于不同的协议(http
vs https
),旧方法也会给出不准确的结果。
Here's my slightly modified version:
这是我稍微修改的版本:
var checkDomain = function(url) {
if ( url.indexOf('//') === 0 ) { url = location.protocol + url; }
return url.toLowerCase().replace(/([a-z])?:\/\//,'').split('/')[0];
};
var isExternal = function(url) {
return ( ( url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url) );
};
Here are some tests with the updated function:
以下是更新功能的一些测试:
isExternal('http://google.com'); // true
isExternal('https://google.com'); // true
isExternal('//google.com'); // true (no protocol)
isExternal('mailto:[email protected]'); // 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
It's more accurate overall, and it even ends up being marginally faster, according to some basic jsperf tests. If you leave off the .toLowerCase()
for case-insensitive testing, you can speed it up even more.
根据一些基本的jsperf 测试,它总体上更准确,甚至会稍微快一点。如果您.toLowerCase()
不使用不区分大小写的测试,则可以进一步加快速度。
回答by Jon
pseudosavant's answer didn't exactly work for me, so I improved it.
伪学者的回答对我来说并不完全有效,所以我改进了它。
var isExternal = function(url) {
return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]);
}
回答by André-Luc Huneault
For my purpose I just did a little modification to shshaw's answer to verify if links are not empty or just a single character (supposing it's '#'), which original answer method returns false positive. This was for my purpose to indicate to users they will leave my page by adding some FA icon.
出于我的目的,我只是对 shshaw 的答案做了一些修改,以验证链接是否为空或只是一个字符(假设它是“#”),原始答案方法返回误报。这是为了向用户表明他们将通过添加一些 FA 图标离开我的页面。
// same thing here, no edit
function checkDomain(url) {
if ( url.indexOf('//') === 0 ) { url = location.protocol + url; }
return url.toLowerCase().replace(/([a-z])?:\/\//,'').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> ");
}
});
}
回答by BumbleB2na
I had to build on pseudosavant's and Jon's answers because, I needed to also catch cases of URLs beginning with "//" and URLs that do not include a sub-domain. Here's what worked for me:
我必须建立在pseudosavant 和Jon 的答案上,因为我还需要捕获以“//”开头的URL 和不包含子域的URL 的情况。以下是对我有用的内容:
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');
});
<h3>Internal URLs:</h3>
<ul>
<li><a href="stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls" target="_blank">stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls</a></li>
<li><a href="www.stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls" target="_blank">www.stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls</a></li>
<li><a href="//stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls" target="_blank">//stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls</a></li>
<li><a href="//www.stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls" target="_blank">//www.stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls</a></li>
</ul>
<h3>External URLs:</h3>
<ul>
<li><a href="http://www.yahoo.com" target="_blank">http://www.yahoo.com</a></li>
<li><a href="yahoo.com" target="_blank">yahoo.com</a></li>
<li><a href="www.yahoo.com" target="_blank">www.yahoo.com</a></li>
<li><a href="//www.yahoo.com" target="_blank">//www.yahoo.com</a></li>
</ul>
回答by user3116736
Shouldn't
不应该
function is_external( url ) {
return url.match( /[a-zA-Z0-9]*:\/\/[^\s]*/g ) != null;
}
do the trick? Doesn't work for absolute (internal) urls.
做的伎俩?不适用于绝对(内部)网址。
回答by mrded
The main problem, is how to parse an URL, and get a host name our of it. It can be done with following way:
主要问题是如何解析一个 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'));
Or you can use is-url-externalmodule.
或者您可以使用is-url-external模块。
var isExternal = require('is-url-external');
isExternal('http://john.doe'); // true | false
回答by muhajirframe
You can simply use use npm package is-internal-link
您可以简单地使用 use npm package is-internal-link
Installation
安装
npm install --save is-internal-link
Usage
用法
import { isInternalLink } from "is-internal-link"
isInternalLink('https://www.google.com') // false
isInternalLink('/page1') // true
I also usually this with react like this
我通常也这样反应
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
Disclaimer: I am the author of this lib
免责声明:我是这个库的作者