web-dev-qa-db-fra.com

Convertir le chemin relatif en absolu avec JavaScript

Il y a une fonction qui me donne des URL comme:

./some.css
./extra/some.css
../../lib/slider/slider.css

C'est toujours un chemin relatif.

Imaginons que nous connaissions le chemin actuel de la page, comme http://site.com/stats/2012/, ne sachant pas comment convertir ces chemins relatifs en chemins réels.

Nous devrions obtenir quelque chose comme:

./some.css => http://site.com/stats/2012/some.css
./extra/some.css => http://site.com/stats/2012/extra/some.css
../../lib/slider/slider.css => http://site.com/lib/slider/slider.css

Pas de jQuery, seulement du javascript à la vanille.

49
Jasper

Cela devrait le faire:

function absolute(base, relative) {
    var stack = base.split("/"),
        parts = relative.split("/");
    stack.pop(); // remove current file name (or empty string)
                 // (omit if "base" is the current folder without trailing slash)
    for (var i=0; i<parts.length; i++) {
        if (parts[i] == ".")
            continue;
        if (parts[i] == "..")
            stack.pop();
        else
            stack.Push(parts[i]);
    }
    return stack.join("/");
}
36
Bergi

Javascript le fera pour vous. Pas besoin de créer une fonction. pas besoin de définir le nom de base.

var link = document.createElement("a");
link.href = "../../lib/slider/slider.css";
alert(link.protocol+"//"+link.Host+link.pathname+link.search+link.hash);

//output will be "http://www.yoursite.com/lib/slider/slider.css"

Si vous avez besoin d’une fonction, encapsulez-la comme une fonction avec 3 lignes de code.

var absolutePath = function(href) {
    var link = document.createElement("a");
    link.href = href;
    return (link.protocol+"//"+link.Host+link.pathname+link.search+link.hash);
}

---- MIS À JOUR ---
Version plus simple seulement si vous avez besoin du chemin absolu

var absolutePath = function(href) {
    var link = document.createElement("a");
    link.href = href;
    return link.href;
}
59
allenhwkim

La manière la plus simple, efficace et correcte de le faire consiste simplement à utiliser URL api.

new URL("http://www.stackoverflow.com?q=hello").href;
//=> http://www.stackoverflow.com/?q=hello"

new URL("mypath","http://www.stackoverflow.com").href;
//=> "http://www.stackoverflow.com/mypath"

new URL("../mypath","http://www.stackoverflow.com/search").href
//=> "http://www.stackoverflow.com/mypath"

new URL("../mypath", window.location.href).href
//=> "https://stackoverflow.com/questions/mypath"

En termes de performances, cette solution est comparable à la manipulation de chaînes et deux fois plus rapide que la création de la balise a.

45
Elad

Ceci de MDN est incassable!

/*\
|*|
|*|  :: translate relative paths to absolute paths ::
|*|
|*|  https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|*|
|*|  The following code is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
\*/

function relPathToAbs (sRelPath) {
  var nUpLn, sDir = "", sPath = location.pathname.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, "$1"));
  for (var nEnd, nStart = 0; nEnd = sPath.indexOf("/../", nStart), nEnd > -1; nStart = nEnd + nUpLn) {
    nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
    sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp("(?:\\\/+[^\\\/]*){0," + ((nUpLn - 1) / 3) + "}$"), "/");
  }
  return sDir + sPath.substr(nStart);
}

Exemple d'utilisation:

/* Let us be in /en-US/docs/Web/API/document.cookie */

alert(location.pathname);
// displays: /en-US/docs/Web/API/document.cookie

alert(relPathToAbs("./"));
// displays: /en-US/docs/Web/API/

alert(relPathToAbs("../Guide/API/DOM/Storage"));
// displays: /en-US/docs/Web/Guide/API/DOM/Storage

alert(relPathToAbs("../../Firefox"));
// displays: /en-US/docs/Firefox

alert(relPathToAbs("../Guide/././API/../../../Firefox"));
// displays: /en-US/docs/Firefox
6
madmurphy

Si vous souhaitez effectuer une conversion relative en absolu pour un lien d'une page Web personnalisée dans votre navigateur (pas pour la page qui exécute votre script), vous pouvez utiliser une version plus améliorée de la fonction proposée par @Bergi: 

var resolveURL=function resolve(url, base){
    if('string'!==typeof url || !url){
        return null; // wrong or empty url
    }
    else if(url.match(/^[a-z]+\:\/\//i)){ 
        return url; // url is absolute already 
    }
    else if(url.match(/^\/\//)){ 
        return 'http:'+url; // url is absolute already 
    }
    else if(url.match(/^[a-z]+\:/i)){ 
        return url; // data URI, mailto:, tel:, etc.
    }
    else if('string'!==typeof base){
        var a=document.createElement('a'); 
        a.href=url; // try to resolve url without base  
        if(!a.pathname){ 
            return null; // url not valid 
        }
        return 'http://'+url;
    }
    else{ 
        base=resolve(base); // check base
        if(base===null){
            return null; // wrong base
        }
    }
    var a=document.createElement('a'); 
    a.href=base;

    if(url[0]==='/'){ 
        base=[]; // rooted path
    }
    else{ 
        base=a.pathname.split('/'); // relative path
        base.pop(); 
    }
    url=url.split('/');
    for(var i=0; i<url.length; ++i){
        if(url[i]==='.'){ // current directory
            continue;
        }
        if(url[i]==='..'){ // parent directory
            if('undefined'===typeof base.pop() || base.length===0){ 
                return null; // wrong url accessing non-existing parent directories
            }
        }
        else{ // child directory
            base.Push(url[i]); 
        }
    }
    return a.protocol+'//'+a.hostname+base.join('/');
}

Il retournera null si quelque chose ne va pas. 

Usage:

resolveURL('./some.css', 'http://example.com/stats/2012/'); 
// returns http://example.com/stats/2012/some.css

resolveURL('extra/some.css', 'http://example.com/stats/2012/');
// returns http://example.com/stats/2012/extra/some.css

resolveURL('../../lib/slider/slider.css', 'http://example.com/stats/2012/');
// returns http://example.com/lib/slider/slider.css

resolveURL('/rootFolder/some.css', 'https://example.com/stats/2012/');
// returns https://example.com/rootFolder/some.css

resolveURL('localhost');
// returns http://localhost

resolveURL('../non_existing_file', 'example.com')
// returns null
5
optimizitor
function canonicalize(url) {
    var div = document.createElement('div');
    div.innerHTML = "<a></a>";
    div.firstChild.href = url; // Ensures that the href is properly escaped
    div.innerHTML = div.innerHTML; // Run the current innerHTML back through the parser
    return div.firstChild.href;
}

Cela fonctionne aussi sur IE6, contrairement à d’autres solutions (voir Obtenir une URL absolue à partir d’une adresse relative. (Problème avec IE6) )

3
Sebastien Lorber

La solution href ne fonctionne que lorsque le document est chargé (au moins dans IE11). Cela a fonctionné pour moi:

link = link || document.createElement("a");
link.href = window.location.href + "/../" + href;
return link.href;
2
Corey Alix

La solution proposée et acceptée ne prend pas en charge les URL relatives du serveur et ne fonctionne pas sur les URL absolues . Si mon parent est/sites/folder1, cela ne fonctionnera pas par exemple.

Voici une autre fonction qui prend en charge les URL complètes, relatives ou relatives du serveur, ainsi que ../ pour un niveau supérieur. Ce n'est pas parfait, mais couvre de nombreuses options ..__ Utilisez cette option lorsque votre URL de base n'est pas l'URL de la page actuelle, sinon il existe de meilleures alternatives.

    function relativeToAbsolute(base, relative) {
    //make sure base ends with /
    if (base[base.length - 1] != '/')
        base += '/';

    //base: https://server/relative/subfolder/
    //url: https://server
    let url = base.substr(0, base.indexOf('/', base.indexOf('//') + 2));
    //baseServerRelative: /relative/subfolder/
    let baseServerRelative = base.substr(base.indexOf('/', base.indexOf('//') + 2));
    if (relative.indexOf('/') === 0)//relative is server relative
        url += relative;
    else if (relative.indexOf("://") > 0)//relative is a full url, ignore base.
        url = relative;
    else {
        while (relative.indexOf('../') === 0) {
            //remove ../ from relative
            relative = relative.substring(3);
            //remove one part from baseServerRelative. /relative/subfolder/ -> /relative/
            if (baseServerRelative !== '/') {
                let lastPartIndex = baseServerRelative.lastIndexOf('/', baseServerRelative.length - 2);
                baseServerRelative = baseServerRelative.substring(0, lastPartIndex + 1);
            }
        }
        url += baseServerRelative + relative;//relative is a relative to base.
    }

    return url;
}

J'espère que cela t'aides. C'était vraiment frustrant de ne pas avoir cet utilitaire de base disponible en JavaScript.

1
Shai Petel

J'ai trouvé une solution très simple pour le faire tout en prenant en charge IE 10 (IE ne prend pas en charge l'URL-API) en utilisant History API (IE 10 ou une version supérieure). Cette solution fonctionne sans aucune manipulation de chaîne.

function resolveUrl(relativePath) {
    var originalUrl = document.location.href;
    history.replaceState(history.state, '', relativePath);
    var resolvedUrl = document.location.href;
    history.replaceState(history.state, '', originalUrl);
    return resolvedUrl;
}

history.replaceState() ne déclenchera pas la navigation dans le navigateur, mais modifiera toujours document.location et supportera les chemins relatifs ainsi que les chemins absolus. 

L'inconvénient de cette solution est que si vous utilisez déjà l'historique-API et avez défini un état personnalisé avec un titre, le titre de l'état actuel est perdu.

0
whY

Cela fonctionnera. mais seulement lorsque vous ouvrez une page avec son nom de fichier. cela ne fonctionnera pas bien quand vous ouvrez un lien comme celui-ci stackoverflow.com/page. cela fonctionnera avec stackoverflow.com/page/index.php

function reltoabs(link){
    let absLink = location.href.split("/");
    let relLink = link;
    let slashesNum = link.match(/[.]{2}\//g) ? link.match(/[.]{2}\//g).length : 0;
    for(let i = 0; i < slashesNum + 1; i++){
        relLink = relLink.replace("../", "");
        absLink.pop();
    }
    absLink = absLink.join("/");
    absLink += "/" + relLink;
    return absLink;
}
0
Mohamed hesham

J'ai dû ajouter un correctif à la solution acceptée car nous pouvons avoir des barres obliques après # dans notre navigation angularjs.

function getAbsoluteUrl(base, relative) {
  // remove everything after #
  var hashPosition = base.indexOf('#');
  if (hashPosition > 0){
    base = base.slice(0, hashPosition);
  }

  // the rest of the function is taken from http://stackoverflow.com/a/14780463
  // http://stackoverflow.com/a/25833886 - this doesn't work in cordova
  // http://stackoverflow.com/a/14781678 - this doesn't work in cordova
  var stack = base.split("/"),
      parts = relative.split("/");
  stack.pop(); // remove current file name (or empty string)
               // (omit if "base" is the current folder without trailing slash)
  for (var i=0; i<parts.length; i++) {
    if (parts[i] == ".")
      continue;
    if (parts[i] == "..")
      stack.pop();
    else
      stack.Push(parts[i]);
  }
  return stack.join("/");
}
0
Stanislav