Отримання абсолютної URL-адреси з відносної. (Випуск IE6)


80

На даний момент я використовую таку функцію для "перетворення" відносної URL-адреси в абсолютну:

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

Це працює досить добре в більшості браузерів, але IE6 наполягає на тому, щоб повернути відносну URL-адресу все-таки! Це робить те саме, якщо я використовую getAttribute ('href').

Єдиний спосіб, яким я зміг отримати кваліфіковану URL-адресу з IE6, - це створити елемент img і запитати його атрибут 'src' - проблема в тому, що він генерує запит сервера; чогось, чого я хочу уникати.

Отже, моє запитання: чи є спосіб отримати повноцінну URL-адресу в IE6 з відносної (без запиту сервера)?


Перш ніж ви порекомендуєте швидко виправити регулярний вираз / рядок, я запевняю вас, це не так просто. Базові елементи + відносні URL-адреси з подвійним періодом + тонна інших потенційних змінних справді роблять це пеклом!

Повинен бути спосіб зробити це без необхідності створювати мамонта регулярного розчину ??


1
Ви можете використовувати js-uri, щоб відновити відносний URI до абсолютного.
Gumbo

Дякую, Гамбо, я гадаю, це доведеться зробити. Я хотів би отримати більш стисле рішення, але все одно дякую, я ніколи не знав, що існує цей клас js-uri!
Джеймс

8
Солодкий хак! Не дбайте про IE6. Врятував мені години. Ви рок.
Том Гаррісон,

Я не працював з цим, у мене просто "foo", і я хочу " example.com/foo "
Хайме Хаблуцель,

Здається, бібліотека js-uri робить не те, що хоче оригінальний плакат.
djsmith

Відповіді:


47

Як дивно! Однак IE це розуміє, коли ви використовуєте innerHTML замість методів DOM.

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

Трохи потворно, але коротше, ніж робити це самостійно.


Я знайшов це подібне рішення в блозі, якому не потрібен код для втечі: stackoverflow.com/a/22918332/82609
Себастьян Лорбер

Цей підхід замінює null (U + 0000) на (U + FFFD), відповідно до специфікації HTML .
Oriol

26

Поки браузер правильно реалізує тег <base>, до якого браузери прагнуть:

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

Ось jsfiddle, де ви можете експериментувати з ним: http://jsfiddle.net/ecmanaut/RHdnZ/


Партія запізнюється на три роки, тож це займе деякий час, щоб піднятися на вершину, не маючи маркетингу та безлічі людей, які мають проблему, і не бажають консервативного та точного рішення.
екманав

2
Окрім підтримки довільних базових URL-адрес, чим саме це відрізняється від рішення, представленого у питанні? Чи працює це на IE 6?
Джон

1
@AmadeusDrZaius Не слід, але вони можуть бути, якщо хочете. Javascript додає автоматичну крапку з комою лише в кінці рядка, коли це не робить майбутній рядок недійсним твердженням. ", foo = 1" - це синтаксична помилка, і, отже, весь оператор var обробляється масово, без введення крапки з комою.
екманав

2
@AndreasDietrich Це тому, що ви не передаєте жоден аргумент base_urlпараметру, тому він стає undefinedта розтягується на "undefined". Натомість слід передати порожній рядок. Або, якщо ви хочете зробити 2-й параметр необов’язковим, використовуйте our_base.href = base_url || ""замість our_base.href = base_url..
Oriol

1
Хороша ідея, @Oriol - немає причин не мати більш дружньої поведінки за замовчуванням для людей, які не передають обидва параметри. Інтегрований.
екманав

16

Ви можете змусити його працювати на IE6, просто клонуючи елемент:

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

(Тестується за допомогою IETester в режимах IE6 та IE5.5)


10

У цьому блозі я знайшов ще один метод, який справді схожий на рішення @bobince.

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

Я знайшов це трохи більш елегантним, не великою справою.


7

Схоже, URI.js вирішує проблему:

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

Див. Також http://medialize.github.io/URI.js/docs.html#absoluteto

Не тестується з IE6, але може бути корисним для інших, хто шукає загальну проблему.


1
На стороні вузла (для сканування тощо), тут доступна правильна бібліотека npm install URIjs, а не інша бібліотека з подібною назвою
y3sh

назва пакета npm змінено на urijs github.com/medialize/URI.js#using-urijs
Даніель Лізік

7

Я насправді хотів підходу до цього, який не вимагав би модифікації оригінального документа (навіть тимчасово), але все ж використовував вбудований синтаксичний аналіз URL-адреси та подібні. Крім того, я хотів мати можливість надати власну базу (як відповідь екманаху). Це досить просто, але використовує createHTMLDocument (можна замінити на createDocument, щоб бути трохи суміснішим):

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/


1
Не впевнений, що мені чогось не вистачає, але IE6 (ні 7, 8) не підтримуєdocument.implementation.createHTMLDocument
Oriol

Я використовував це, коли використовував веб-програму для завантаження та зішкрябування інших сторінок. Під час зворотного виклику з jQuery.load $("#loadedHere").createElement("a").url="foo"виникла порожня URL-адреса, тому мені довелося вдатися до створення окремого документа.
ericP

5

Це рішення працює у всіх браузерах.

/**
 * 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

Однак він не підтримує відносні URL-адреси з "..", наприклад "../file.png".


Це має деякі проблеми. Наприклад, ви припускаєте, що база така сама, як і windows, і я не думаю, що це працює, якщо у мене є параметр url в url. Say /img/profile.php?url=https://google.com/logo.svg.
Теодор

3

Це функція, яку я використовую для вирішення основних відносних URL-адрес:

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(/^\.\//, '');
}

Перевірте на jsfiddle: https://jsfiddle.net/n11rg255/

Він працює як у браузері, так і в node.js або інших середовищах.


2

Я знайшов цю публікацію в блозі, яка пропонує використовувати елемент зображення замість якоря:

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

Це працює для надійного розширення URL-адреси, навіть у IE6. Але проблема в тому, що перевірені мною браузери негайно завантажать ресурс після встановлення атрибута image src - навіть якщо ви встановите для src значення null у наступному рядку.

Натомість я збираюся дати рішення бобінце.


0

Якщо urlне починається з '/'

Візьміть URL-адресу поточної сторінки, відріжте все, що минуло останнє '/'; потім додайте відносну URL-адресу.

В іншому випадку, якщо urlпочинається з '/'

Візьміть URL-адресу поточної сторінки та відріжте все праворуч від синглу '/'; потім додайте URL-адресу.

Інакше, якщо urlпочинається з # або?

Візьміть URL-адресу поточної сторінки та просто додайте url


Сподіваюся, це працює для вас


2
Ви забули, що URL-адреси можуть починатися з "//", що робить їх відносними до схеми. //foo.com/bar/
Скотт Волчок

1
ви також забули пунктирний відносний синтаксис ../../ (чи має значення цей пропуск чи ні, залежить від того, для чого потрібний результат)
зал засідань

-1

Якщо він працює у браузері, це працює для мене ..

  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;
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.