Vlas Bashynskyi Vlas Bashynskyi - 1 month ago 7
Javascript Question

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?

Answer

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