Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paths in Web Components are Relative to Root

I am creating a web component using native implementation, which in it's html template has links to images. However, those links only work if they are absolute, or relative to the main document, which means, that that component is not reusable, or portable. Also, it is very counterintuitive.

Currently, I add a data-url_prefix attribute to all elements that need to use images. Then, when creating a shadow root for my custom element, I replace a {{URL_PREFIX}} with the value of that argument.

My solution seems very bad. I would be very glad if you advised something better, thanks.


I found an interesting quote on the http://webcomponents.org/polyfills/html-imports/ page:

POLYFILL NOTES

In imported documents, href and src attributes in HTML, and url properties in CSS files, are relative to the location of the imported document, not the main document.

why would a polifill use different logic that the native implementation?


Web Components Ideally should encapsulate all their dependencies, but if a web component requires an image, it should know the absolute URL to that image, which does not allow the component to be simply moved around in file structure.

Say, for example I have the following structure:

  • index.html
  • css
    • main.css
  • js
    • main.js
  • web_components
    • cool_web_component
      • cool_web_component.html
      • icon.png

If I change it to the following:

  • index.html
  • css
    • main.css
  • js
    • main.js
  • cool_web_component
    • cool_web_component.html
    • icon.png

I would be required to change the pointer to icon.png somewhere in those files My question is how to avoid it, or solve it in elegant way. Also, why the actual native implementation is in conflict with the polyfills?

like image 520
Vlas Bashynskyi Avatar asked Oct 31 '15 18:10

Vlas Bashynskyi


People also ask

What is a root relative path?

Site root–relative paths describe the path from the site's root folder to a document. You may want to use these paths if you are working on a large website that uses several servers, or one server that hosts several sites.

What is relative path?

A relative path refers to a location that is relative to a current directory. Relative paths make use of two special symbols, a dot (.) and a double-dot (..), which translate into the current directory and the parent directory. Double dots are used for moving up in the hierarchy.

What is relative path in angular?

So when we change the parent route then we have to change the path to reflect the links used outside also. So by the use of relative path you are making your links free that are relative to the current URL segment. Your feature area of routing will be the same even if you change the route for the parent.


2 Answers

The webcomponent specification defines that URLs are always relative to the main document. This, of course, breaks web component encapsulation, as you rightly concluded. In other words, the specification is buggy, and lots of people are complaining about it. That's why the polyfill doesn't follow the specification: it's fixing the problem.

The specification will change. However, since this is not trivial to fix, it could still take some time. Please see these links:

• https://lists.w3.org/Archives/Public/public-webapps/2014OctDec/0013.html

• https://www.w3.org/Bugs/Public/show_bug.cgi?id=20976#c8

For the moment, the solution is for your component to change the URL of its template images, from relative to absolute. You can get the templateBaseUrl as follows:

(function (window, document)
    {
    var proto = Object.create(HTMLElement.prototype);

    var template = document.currentScript.ownerDocument.querySelector("template");
    var templateBaseUrl = template.baseURI;

    proto.createdCallback = function ()
        {
        // Now find all images inside the template and change their src attribute, 
        // using templateBaseUrl. Also you must change CSS background-image: url(...);
        ...
        };

    document.registerElement('test-element', {prototype: proto});
    })(window, document);

Another way of fixing this, in case of small images like icons, is to embed the image data directly into the document, with data URIs. This also saves HTTP Requests (until we have Http/2). For example:

<img src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" />
like image 173
MarcG Avatar answered Oct 01 '22 02:10

MarcG


This behavior seems to be specific to images.
For script and link tags, relative paths from imported document work as expected.

Also I noticed that this is not something specific to polyfills, even for native implementation(chrome) this issue(?) seems to be there.

Seems like only option here is to include a script in your imported html, which will convert these relative paths to their absolute counterparts.
To solve it elegantly you can avoid hardcoding urls in your script and generate it using url of your importing document. You can get that from document.currentScript.ownerDocument(or document._currentScript.ownerDocument in polyfilled scenario).

So to answer your second question, I don't see any difference in native and polyfilled implementation at least in terms of behavior of src and href attributes.
Quote you mentioned from http://webcomponents.org/polyfills/html-imports/ seems to be specific to href/src of script and link tags and they work as written.

Hope it helps.

like image 43
Abhinav Avatar answered Oct 01 '22 04:10

Abhinav