Abrufen einer absoluten URL von einer relativen. (IE6-Problem)

80

Ich verwende derzeit die folgende Funktion, um eine relative URL in eine absolute zu konvertieren:

function qualifyURL(url) {
    var a = document.createElement('a');
    a.href = url;
    return a.href;
}

Dies funktioniert in den meisten Browsern recht gut, aber IE6 besteht darauf, die relative URL trotzdem zurückzugeben! Das gleiche gilt, wenn ich getAttribute ('href') verwende.

Die einzige Möglichkeit, eine qualifizierte URL aus IE6 zu erhalten, besteht darin, ein img-Element zu erstellen und das Attribut 'src' abzufragen. Das Problem dabei ist, dass eine Serveranforderung generiert wird. etwas, das ich vermeiden möchte.

Meine Frage lautet also: Gibt es eine Möglichkeit, eine vollständig qualifizierte URL in IE6 von einer relativen URL (ohne Serveranforderung) abzurufen?


Bevor Sie eine schnelle Regex- / String-Korrektur empfehlen, versichere ich Ihnen, dass dies nicht so einfach ist. Basiselemente + relative URLs mit doppelter Periode + eine Tonne anderer potenzieller Variablen machen es wirklich zur Hölle!

Es muss einen Weg geben, dies zu tun, ohne ein Mammut einer Regex'y-Lösung erstellen zu müssen?

James
quelle
1
Sie können js-uri verwenden , um den relativen URI in einen absoluten aufzulösen.
Gumbo
Danke Gumbo, ich nehme an, das muss reichen. Ich hätte mir eine präzisere Lösung gewünscht, aber trotzdem danke, ich wusste nie, dass es diese js-uri-Klasse gibt!
James
8
Süßer Hack! IE6 ist mir egal. Hat mir Stunden gespart. Du rockst.
Tom Harrison
Ich habe es nicht zum
Laufen gebracht
Die js-uri-Bibliothek scheint nicht das zu tun, was das Originalplakat will.
DJsmith

Antworten:

47

Wie seltsam! IE versteht es jedoch, wenn Sie innerHTML anstelle von DOM-Methoden verwenden.

function escapeHTML(s) {
    return s.split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
}
function qualifyURL(url) {
    var el= document.createElement('div');
    el.innerHTML= '<a href="'+escapeHTML(url)+'">x</a>';
    return el.firstChild.href;
}

Ein bisschen hässlich, aber prägnanter als es selbst zu tun.

Bobince
quelle
Ich habe diese ähnliche Lösung in einem Blog gefunden, für dessen Flucht kein Code erforderlich ist
Sebastien Lorber
Dieser Ansatz ersetzt null (U + 0000) durch (U + FFFD) gemäß HTML-Spezifikation .
Oriol
26

Solange der Browser das <base> -Tag korrekt implementiert, neigen die Browser dazu:

function resolve(url, base_url) {
  var doc      = document
    , old_base = doc.getElementsByTagName('base')[0]
    , old_href = old_base && old_base.href
    , doc_head = doc.head || doc.getElementsByTagName('head')[0]
    , our_base = old_base || doc_head.appendChild(doc.createElement('base'))
    , resolver = doc.createElement('a')
    , resolved_url
    ;
  our_base.href = base_url || '';
  resolver.href = url;
  resolved_url  = resolver.href; // browser magic at work here

  if (old_base) old_base.href = old_href;
  else doc_head.removeChild(our_base);
  return resolved_url;
}

Hier ist eine jsfiddle, mit der Sie experimentieren können: http://jsfiddle.net/ecmanaut/RHdnZ/

ecmanaut
quelle
Es ist drei Jahre zu spät für die Party, daher wird es eine Weile dauern, bis die Spitze erreicht ist, ohne dass Marketing oder viele Leute das Problem haben und eine codekonservative und genaue Lösung wünschen.
Ecmanaut
2
Wie genau unterscheidet sich dies von der in der Frage dargestellten Lösung, abgesehen von der Unterstützung beliebiger Basis-URLs? Funktioniert es auf IE 6?
John
1
@AmadeusDrZaius Nicht sollte, aber sie können sein, wenn Sie möchten. Javascript fügt nur am Ende einer Zeile ein automatisches Semikolon hinzu, wenn dies nicht dazu führt, dass die kommende Zeile zu einer ungültigen Anweisung wird. ", foo = 1" ist ein Syntaxfehler, und daher wird die gesamte var-Anweisung in Massen ohne Semikolon-Einfügung ausgewertet.
Ecmanaut
2
@AndreasDietrich Das liegt daran, dass Sie dem base_urlParameter kein Argument übergeben , sodass er zu einem undefinedString wird "undefined". Sie sollten stattdessen die leere Zeichenfolge übergeben. Oder, wenn Sie den 2. Parameter optional machen möchten, verwenden Sie our_base.href = base_url || ""anstelle von our_base.href = base_url..
Oriol
1
Gute Idee, @Oriol - kein Grund, kein freundlicheres Standardverhalten für Personen zu haben, die nicht beide Parameter übergeben. Integriert.
Ecmanaut
16

Sie können es auf IE6 zum Laufen bringen, indem Sie einfach das Element klonen:

function qualifyURL(url) {
    var a = document.createElement('a');
    a.href = url;
    return a.cloneNode(false).href;
}

(Getestet mit IETester im IE6- und IE5.5-Modus)

Oriol
quelle
10

Ich habe in diesem Blog eine andere Methode gefunden, die wirklich wie eine @ bbince-Lösung aussieht.

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

Ich fand es etwas eleganter, keine große Sache.

Sebastien Lorber
quelle
7

URI.js scheint das Problem zu lösen:

URI("../foobar.html").absoluteTo("http://example.org/hello/world.html").toString()

Siehe auch http://medialize.github.io/URI.js/docs.html#absoluteto

Nicht mit IE6 getestet, aber möglicherweise hilfreich für andere, die nach dem allgemeinen Problem suchen.

koppor
quelle
1
Auf der Knotenseite der Dinge (zum Crawlen usw.) ist die richtige Bibliothek hier über verfügbar npm install URIjs, nicht die andere Bibliothek mit ähnlichem Namen
y3sh
Das genannte npm-Paket wurde in urijs github.com/medialize/URI.js#using-urijs
Daniel Lizik
7

Eigentlich wollte ich einen Ansatz, der keine Änderung des Originaldokuments (nicht einmal vorübergehend) erfordert, aber dennoch die integrierte URL-Analyse des Browsers und dergleichen verwendet. Außerdem wollte ich in der Lage sein, meine eigene Basis bereitzustellen (wie die Antwort von ecmanaught). Es ist ziemlich einfach, verwendet aber createHTMLDocument (könnte durch createDocument ersetzt werden, um möglicherweise ein bisschen kompatibler zu sein):

function absolutize(base, url) {
    d = document.implementation.createHTMLDocument();
    b = d.createElement('base');
    d.head.appendChild(b);
    a = d.createElement('a');
    d.body.appendChild(a);
    b.href = base;
    a.href = url;
    return a.href;
}

http://jsfiddle.net/5u6j403k/

Chris Hopman
quelle
1
Ich bin mir nicht sicher, ob mir etwas fehlt, aber IE6 (noch 7, 8) unterstützt nichtdocument.implementation.createHTMLDocument
Oriol
Ich habe dies verwendet, als ich eine Web-App verwendet habe, um andere Seiten zu laden und zu kratzen. Der Rückruf von jQuery.load $("#loadedHere").createElement("a").url="foo"führte zu einer leeren URL, sodass ich ein separates Dokument erstellen musste.
EricP
5

Diese Lösung funktioniert in allen Browsern.

/**
 * Given a filename for a static resource, returns the resource's absolute
 * URL. Supports file paths with or without origin/protocol.
 */
function toAbsoluteURL (url) {
  // Handle absolute URLs (with protocol-relative prefix)
  // Example: //domain.com/file.png
  if (url.search(/^\/\//) != -1) {
    return window.location.protocol + url
  }

  // Handle absolute URLs (with explicit origin)
  // Example: http://domain.com/file.png
  if (url.search(/:\/\//) != -1) {
    return url
  }

  // Handle absolute URLs (without explicit origin)
  // Example: /file.png
  if (url.search(/^\//) != -1) {
    return window.location.origin + url
  }

  // Handle relative URLs
  // Example: file.png
  var base = window.location.href.match(/(.*\/)/)[0]
  return base + url

Relative URLs mit ".." wie "../file.png" werden jedoch nicht unterstützt.

Feross
quelle
Dies hat einige Probleme. Sie gehen beispielsweise davon aus, dass die Basis mit Windows identisch ist, und ich glaube nicht, dass dies funktioniert, wenn ich einen URL-Parameter in der URL habe. Sagen Sie /img/profile.php?url=https://google.com/logo.svg.
Teodors
3

Dies ist die Funktion, mit der ich grundlegende relative URLs auflöse:

function resolveRelative(path, base) {
    // Absolute URL
    if (path.match(/^[a-z]*:\/\//)) {
      return path;
    }
    // Protocol relative URL
    if (path.indexOf("//") === 0) {
      return base.replace(/\/\/.*/, path)
    }
    // Upper directory
    if (path.indexOf("../") === 0) {
        return resolveRelative(path.slice(3), base.replace(/\/[^\/]*$/, ''));
    }
    // Relative to the root
    if (path.indexOf('/') === 0) {
        var match = base.match(/(\w*:\/\/)?[^\/]*\//) || [base];
        return match[0] + path.slice(1);
    }
    //relative to the current directory
    return base.replace(/\/[^\/]*$/, "") + '/' + path.replace(/^\.\//, '');
}

Testen Sie es auf jsfiddle: https://jsfiddle.net/n11rg255/

Es funktioniert sowohl im Browser als auch in node.js oder anderen Umgebungen.

Lovasoa
quelle
2

Ich habe diesen Blog-Beitrag gefunden, in dem vorgeschlagen wird, ein Bildelement anstelle eines Ankers zu verwenden:

http://james.padolsey.com/javascript/getting-a-fully-qualified-url/

Das funktioniert, um eine URL auch in IE6 zuverlässig zu erweitern. Das Problem ist jedoch, dass die von mir getesteten Browser die Ressource beim Festlegen des Attributs image src sofort herunterladen - selbst wenn Sie den src in der nächsten Zeile auf null setzen.

Ich werde stattdessen Bobince's Lösung ausprobieren.

Jesse Hallett
quelle
0

Wenn urlnicht mit '/' beginnt

Nehmen Sie die URL der aktuellen Seite und hacken Sie alles nach dem letzten '/' ab. Fügen Sie dann die relative URL hinzu.

Sonst, wenn urlmit '/' beginnt

Nehmen Sie die URL der aktuellen Seite und hacken Sie alles rechts von der Single '/' ab. Fügen Sie dann die URL hinzu.

Sonst wenn urlmit # oder beginnt?

Nehmen Sie die URL der aktuellen Seite und fügen Sie sie einfach hinzu url


Hoffe es funktioniert bei dir

geowa4
quelle
2
Sie haben vergessen, dass URLs mit "//" beginnen können, wodurch sie schema-relativ werden. //foo.com/bar/
Scott Wolchok
1
Sie haben auch die gepunktete relative ../../ Syntax vergessen (ob diese Auslassung wichtig ist oder nicht, hängt davon ab, wofür die Ausgabe erforderlich ist)
hallvors
-1

Wenn es im Browser läuft, funktioniert diese Art von für mich ..

  function resolveURL(url, base){
    if(/^https?:/.test(url))return url; // url is absolute
    // let's try a simple hack..
    var basea=document.createElement('a'), urla=document.createElement('a');
    basea.href=base, urla.href=url;
    urla.protocol=basea.protocol;// "inherit" the base's protocol and hostname
    if(!/^\/\//.test(url))urla.hostname=basea.hostname; //..hostname only if url is not protocol-relative  though
    if( /^\//.test(url) )return urla.href; // url starts with /, we're done
    var urlparts=url.split(/\//); // create arrays for the url and base directory paths
    var baseparts=basea.pathname.split(/\//); 
    if( ! /\/$/.test(base) )baseparts.pop(); // if base has a file name after last /, pop it off
    while( urlparts[0]=='..' ){baseparts.pop();urlparts.shift();} // remove .. parts from url and corresponding directory levels from base
    urla.pathname=baseparts.join('/')+'/'+urlparts.join('/');
    return urla.href;
  }
Hallvors
quelle