Самый быстрый способ обнаружить внешние URL - PullRequest
24 голосов
/ 04 июня 2011

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

Ответы [ 9 ]

29 голосов
/ 04 июня 2011

Если вы считаете, что 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;
}
17 голосов
/ 17 марта 2012

Я знаю, что версия регулярного выражения уже принята, но могу поспорить, что это "быстрее", чем выполнение этого комплекса регулярных выражений. 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 только один раз внутри замыкания, а не только внутри функции, обычно примерно на 30% быстрее в Firefox.

Вот jsperf , исследующий четыре различных способа определения внешнего имени хоста.

Важно отметить, что каждый метод, который я пробовал, занимает менее 1 мс для запуска даже на старом телефоне. Таким образом, производительность, вероятно, не должна быть вашей основной задачей, если вы не выполняете какую-то большую пакетную обработку.

16 голосов
/ 20 января 2015

Я использовал метод 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 тестам. Если вы отключите .toLowerCase() для тестирования без учета регистра, вы сможете ускорить его еще больше.

4 голосов
/ 12 июля 2013

ответ псевдосаванта мне точно не помог, поэтому я его улучшил.

var isExternal = function(url) {
    return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]);   
}
1 голос
/ 28 июня 2017

Для моей цели я просто внес небольшую модификацию в ответ 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> ");
        }
    });
}
1 голос
/ 29 апреля 2015

Мне пришлось опираться на ответы псевдосаванта и Джона, потому что мне нужно было также отслеживать случаи 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>
0 голосов
/ 13 ноября 2018

Вы можете просто использовать использовать пакет 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

Отказ от ответственности: я являюсь автором этой библиотеки

0 голосов
/ 12 августа 2016

Основная проблема заключается в том, как проанализировать 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 
0 голосов
/ 22 сентября 2015

Разве не должны

function is_external( url ) {
    return url.match( /[a-zA-Z0-9]*:\/\/[^\s]*/g ) != null;
}

делать трюк?Не работает для абсолютных (внутренних) URL.

...