Nhận một URL tuyệt đối từ một URL tương đối. (Vấn đề IE6)


80

Tôi hiện đang sử dụng hàm sau để 'chuyển đổi' một URL tương đối thành URL tuyệt đối:

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

Điều này hoạt động khá tốt trong hầu hết các trình duyệt nhưng IE6 vẫn khăng khăng trả lại URL tương đối! Nó hoạt động tương tự nếu tôi sử dụng getAttribute ('href').

Cách duy nhất tôi có thể lấy một URL đủ điều kiện ra khỏi IE6 là tạo một phần tử img và truy vấn nó thuộc tính 'src' - vấn đề với điều này là nó tạo ra một yêu cầu máy chủ; điều gì đó tôi muốn tránh.

Vì vậy, câu hỏi của tôi là: Có cách nào để lấy URL đủ điều kiện trong IE6 từ một URL tương đối (không có yêu cầu máy chủ) không?


Trước khi bạn đề xuất một bản sửa lỗi regex / string nhanh chóng, tôi đảm bảo với bạn rằng nó không đơn giản như vậy. Các phần tử cơ sở + url tương đối chu kỳ kép + hàng loạt các biến tiềm năng khác thực sự khiến nó trở thành địa ngục!

Phải có một cách để làm điều đó mà không cần phải tạo ra một giải pháp khổng lồ của regex'y ??


1
Bạn có thể sử dụng js-uri để giải quyết URI tương đối thành một URI tuyệt đối.
Gumbo

Cảm ơn Gumbo, tôi cho rằng điều này sẽ phải làm. Tôi sẽ thích một giải pháp ngắn gọn hơn nhưng dù sao cũng cảm ơn bạn, tôi chưa bao giờ biết lớp js-uri này tồn tại!
James

8
Hack ngọt ngào! Đừng quan tâm đến IE6. Đã tiết kiệm cho tôi giờ. Bạn rock.
Tom Harrison

Tôi không nhận nó làm việc với điều này, tôi có chỉ là "foo" và tôi muốn " example.com/foo "
Jaime Hablutzel

Thư viện js-uri dường như không làm được những gì mà người đăng ban đầu muốn.
djsmith 13/12/12

Câu trả lời:


47

Cách kỳ lạ! Tuy nhiên, IE hiểu điều đó khi bạn sử dụng innerHTML thay vì các phương thức 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;
}

Một chút xấu xí, nhưng ngắn gọn hơn Tự làm.


Tôi tìm thấy giải pháp này tương tự như trên một blog mà không cần mã để thoát: stackoverflow.com/a/22918332/82609
Sebastien Lorber

Cách tiếp cận này thay thế null (U + 0000) bằng (U + FFFD), theo thông số kỹ thuật HTML .
Oriol

26

Miễn là trình duyệt triển khai thẻ <base> chính xác, trình duyệt nào có xu hướng:

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

Đây là một jsfiddle mà bạn có thể thử nghiệm với nó: http://jsfiddle.net/ecmanaut/RHdnZ/


Đã muộn 3 năm để đến với bữa tiệc, vì vậy sẽ mất một thời gian để vươn lên dẫn đầu mà không cần tiếp thị hoặc nhiều người gặp vấn đề và muốn một giải pháp chính xác và thận trọng về mã.
ecmanaut

2
Ngoài việc hỗ trợ các URL cơ sở tùy ý, điều này chính xác khác với giải pháp được trình bày trong câu hỏi như thế nào? Nó có hoạt động trên IE 6 không?
John

1
@AmadeusDrZaius Không nên, nhưng họ có thể được nếu bạn thích. Javascript chỉ thêm dấu chấm phẩy tự động vào cuối dòng khi việc này sẽ không làm cho dòng sắp tới trở thành câu lệnh không hợp lệ. ", foo = 1" là một lỗi cú pháp và do đó toàn bộ câu lệnh var được đánh giá hàng loạt, không chèn dấu chấm phẩy.
ecmanaut

2
@AndreasDietrich Đó là bởi vì bạn không truyền bất kỳ đối số nào cho base_urltham số, vì vậy nó trở thành undefinedvà được chuỗi thành "undefined". Thay vào đó, bạn nên chuyển chuỗi trống. Hoặc, nếu bạn muốn đặt tham số thứ 2 là tùy chọn, hãy sử dụng our_base.href = base_url || ""thay vì our_base.href = base_url..
Oriol

1
Ý tưởng hay, @Oriol - không có lý do gì để không có hành vi mặc định thân thiện hơn đối với những người không chuyển cả hai tham số. Tích hợp.
ecmanaut

16

Bạn có thể làm cho nó hoạt động trên IE6 chỉ cần sao chép phần tử:

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

(Đã thử nghiệm bằng IETester trên chế độ IE6 và IE5.5)


10

Tôi đã tìm thấy trên blog này một phương pháp khác thực sự giống như giải pháp @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;
}

Tôi thấy nó thanh lịch hơn một chút, không phải là vấn đề lớn.


7

URI.js dường như giải quyết được vấn đề:

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

Xem thêm http://medialize.github.io/URI.js/docs.html#absoluteto

Không phù hợp với IE6, nhưng có thể hữu ích cho những người khác đang tìm kiếm vấn đề chung.


1
Về phía nút thứ (cho bò, vv), thư viện đúng ở đây là có sẵn thông qua npm install URIjs, không phải là thư viện khác bằng tên tương tự
y3sh

gói npm có tên đã thay đổi thành urijs github.com/medialize/URI.js#using-urijs
Daniel Lizik

7

Tôi thực sự muốn một cách tiếp cận cho điều này mà không yêu cầu sửa đổi tài liệu gốc (thậm chí không phải tạm thời) nhưng vẫn sử dụng phân tích cú pháp url nội trang của trình duyệt và những thứ tương tự. Ngoài ra, tôi muốn có thể cung cấp cơ sở của riêng mình (như câu trả lời của ecmanaught). Nó khá đơn giản, nhưng sử dụng createHTMLDocument (có thể được thay thế bằng createDocument để có thể tương thích hơn một chút):

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
Không chắc liệu tôi có thiếu thứ gì không, nhưng IE6 (cũng không phải 7, 8) không hỗ trợdocument.implementation.createHTMLDocument
Oriol

Tôi đã sử dụng điều này khi sử dụng ứng dụng web để tải và quét các trang khác. Trong lệnh gọi lại từ jQuery.load, $("#loadedHere").createElement("a").url="foo"dẫn đến một url trống vì vậy tôi phải dùng đến cách tạo một tài liệu riêng biệt.
ericP

5

Giải pháp này hoạt động trên tất cả các trình duyệt.

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

Tuy nhiên, nó không hỗ trợ các URL tương đối có ".." trong đó, như "../file.png".


Điều này có một số vấn đề. Ví dụ: bạn đang giả sử cơ sở giống như các cửa sổ và tôi không nghĩ rằng điều này hoạt động nếu tôi có một tham số url trong url. Nói đi /img/profile.php?url=https://google.com/logo.svg.
Teodors

3

Đây là chức năng tôi sử dụng để giải quyết các URL tương đối cơ bản:

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

Kiểm tra nó trên jsfiddle: https://jsfiddle.net/n11rg255/

Nó hoạt động cả trong trình duyệt và node.js hoặc các môi trường khác.


2

Tôi đã tìm thấy bài đăng trên blog này đề xuất sử dụng một phần tử hình ảnh thay vì một liên kết:

http://james.padolsey.com/javascript/getting-a-ly-quained-url/

Điều đó hoạt động để mở rộng một cách đáng tin cậy một URL, ngay cả trong IE6. Nhưng vấn đề là các trình duyệt mà tôi đã thử nghiệm sẽ ngay lập tức tải xuống tài nguyên khi đặt thuộc tính src của hình ảnh - ngay cả khi bạn đặt src thành null trên dòng tiếp theo.

Thay vào đó, tôi sẽ đưa ra giải pháp của bobince.


0

Nếu urlkhông bắt đầu bằng '/'

Lấy url của trang hiện tại, cắt bỏ mọi thứ qua dấu '/' cuối cùng; sau đó nối url tương đối.

Khác nếu urlbắt đầu bằng '/'

Lấy url của trang hiện tại và cắt mọi thứ ở bên phải của trang duy nhất '/'; sau đó nối url.

Khác nếu urlbắt đầu bằng # hoặc?

Lấy url của trang hiện tại và chỉ cần nối url


Hi vọng nó sẽ giúp ích cho bạn


2
Bạn quên rằng các URL có thể bắt đầu bằng "//", điều này làm cho chúng trở thành lược đồ tương đối. //foo.com/bar/
Scott Wolchok

1
bạn cũng quên tương đối cú pháp chấm ../../ (cho dù là thiếu sót này vấn đề hay không phụ thuộc vào những gì mà đầu ra là cần thiết cho)
hallvors

-1

Nếu nó chạy trong trình duyệt, loại này phù hợp với tôi ..

  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;
  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.