Подтвердить что ты не робот

Самый быстрый способ обнаружения внешних URL-адресов

Какой самый быстрый способ обнаружить, если foo='http://john.doe' является внешним url (в соответствии с window.location.href)?

4b9b3361

Ответ 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