Какой самый быстрый способ обнаружить, если foo='http://john.doe'
является внешним url (в соответствии с window.location.href
)?
Самый быстрый способ обнаружения внешних URL-адресов
Ответ 1
Я знаю, что версия regex уже принята, но я бы поспорил, что это "быстрее", чем выполнение этого сложного регулярного выражения. 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
Я решил сделать немного больше исследований по этому вопросу и нашел более быстрый метод, который использует 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);
}
})();
В IE это немного быстрее, чем метод String.replace
. Однако в Chrome и Firefox это примерно в два раза быстрее. Кроме того, определение Regex только один раз внутри закрытия, а не только внутри функции, обычно примерно на 30% быстрее в Firefox.
Вот jsperf, исследующий четыре разных способа определения внешнего имени хоста.
Важно отметить, что каждый метод, который я пытался, занимает менее 1 мс для работы даже на старом телефоне. Таким образом, производительность, вероятно, не должна быть вашим основным соображением, если вы не выполняете большую пакетную обработку.
Ответ 2
Если вы считаете, что 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;
}
Ответ 3
Я использую метод psuedosavant, но столкнулся с несколькими случаями, когда он вызвал ложные срабатывания, такие как ссылки без домена (/about
, image.jpg
) и привязные ссылки (#about
). Старый метод также дал бы неточные результаты для разных протоколов (http
vs 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:[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
Он более точен в целом, и он даже заканчивается чуть быстрее, согласно некоторым базовым jsperf tests. Если вы оставите .toLowerCase()
для проверки без учета регистра, вы можете ускорить его еще больше.
Ответ 4
Ответ pseudosavantне помог мне, поэтому я улучшил его.
var isExternal = function(url) {
return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]);
}
Ответ 5
Мне пришлось строить на псевдозавантах, и Джон отвечает, потому что мне также нужно уловить случаи 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>
Ответ 6
Для моей цели я просто сделал небольшую модификацию для ответа shshaw, чтобы проверить, не являются ли ссылки пустыми или просто один символ (предположим, что это "#" ), причем исходный метод ответа возвращает false positive. Это было для моей цели, чтобы указать пользователям, что они оставят мою страницу, добавив значок 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 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> ");
}
});
}
Ответ 7
Не стоит
function is_external( url ) {
return url.match( /[a-zA-Z0-9]*:\/\/[^\s]*/g ) != null;
}
сделать трюк? Не работает для абсолютных (внутренних) URL-адресов.
Ответ 8
Основная проблема заключается в том, как разобрать 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