Le moyen le plus rapide de détecter les URL externes

Quelle est la méthode la plus rapide pour détecter si foo='http://john.doe' est une URL externe (en comparaison de window.location.href )?

Je sais que la version regex a déjà été acceptée, mais je parie que c'est plus rapide que de faire ce complexe d'une regex. String.replace est assez rapide.

 var isExternal = function(url) { var domain = function(url) { return url.replace('http://','').replace('https://','').split('/')[0]; }; return domain(location.href) !== domain(url); } 

Mettre à jour

J'ai décidé de faire un peu plus de recherches à ce sujet et j'ai trouvé une méthode plus rapide qui utilise un 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); } })(); 

Dans IE, cela est légèrement plus rapide que la méthode String.replace . Cependant, dans Chrome et Firefox, c'est environ deux fois plus rapide. En outre, la définition de Regex qu'une seule fois à l'intérieur de la fermeture au lieu de simplement à l'intérieur de la fonction est normalement d'environ 30% plus rapide dans Firefox.

Voici un jsperf qui examine quatre manières différentes de déterminer un nom d'hôte externe.

Il est important de noter que chaque méthode que j'ai essayé prend moins de 1 ms pour fonctionner même sur un ancien téléphone. Donc, la performance ne devrait probablement pas être votre principale considération, sauf si vous faites un gros traitement par lots.

Si vous considérez qu'une URL est externe si le schéma, l'hôte ou le port est différent, vous pouvez faire quelque chose comme ceci:

 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; } 

J'ai utilisé la méthode de psuedosavant, mais j'ai rencontré quelques cas où il déclenchait des faux positifs, tels que des liens sans domaine ( /about , image.jpg ) et des liens d'ancrage ( #about ). L'ancienne méthode donnerait également des résultats inexacts pour différents protocoles ( http vs https ).

Voici ma version légèrement modifiée:

 var checkDomain = function(url) { if ( url.indexOf('//') === 0 ) { url = location.protocol + url; } return url.toLowerCase().replace(/([az])?:\/\//,'$1').split('/')[0]; }; var isExternal = function(url) { return ( ( url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url) ); }; 

Voici quelques tests avec la fonction mise à jour:

 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 

Il est plus précis dans l'ensemble, et il finit même par être légèrement plus rapide, selon certains tests basiques de jsperf . Si vous quittez .toLowerCase() pour des tests insensibles à la casse, vous pouvez l'accélérer encore plus.

La réponse de pseudosavant n'a pas vraiment fonctionné pour moi, donc je l'ai amélioré.

 var isExternal = function(url) { return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]); } 

Je devais construire sur les réponses de pseudosavant et Jon parce que, j'avais besoin d'attraper des cas d'URL commençant par "//" et des URL qui n'incluent pas un sous-domaine. Voici ce qui a fonctionné pour moi:

 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> 

Pour mon but, je viens de faire une petite modification à la réponse de shshaw pour vérifier si les liens ne sont pas vides ou simplement un seul caractère (en supposant qu'il soit '#'), quelle méthode de réponse originale renvoie les faux positifs. C'était pour mon but d'indiquer aux utilisateurs qu'ils vont laisser ma page en ajoutant une icône FA.

 // same thing here, no edit function checkDomain(url) { if ( url.indexOf('//') === 0 ) { url = location.protocol + url; } return url.toLowerCase().replace(/([az])?:\/\//,'$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> "); } }); } 

Ne devrait pas

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

faire l'affaire? Ne fonctionne pas pour les URL absolues (internes).

Le problème principal, c'est comment analyser une URL, et obtenir un nom d'hôte. On peut le faire de la manière suivante:

 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')); 

Ou vous pouvez utiliser le module externe is-url .

 var isExternal = require('is-url-external'); isExternal('http://john.doe'); // true | false