4

Since html imports are not yet well supported (Firefox, for instance, has no plans to do it), I've tried to mimic its use in importing custom elements via iframes.

The way I've tried to do that is by loading a script in the iframe that defines the custom element in the top browsing context; and the iframe could even be removed from the document after that. The reason I've tried to do so is because I'd like to dynamically define custom elements according to information I get at client side; and those custom elements use templates to be used in the shadow DOM.

I prefer to use templates instead of building the shadow DOM in the script in order to have clean code; that is why I'd like to mimic the import functionality with an iframe with all the templates instead of just loading a script where the shadow DOM is builded.

However, the way I've tried is not working (tested in google chrome and firefox):

// iframe.js

class XAElement extends HTMLElement{
 constructor(){
  super()
  // Any customization here
 }
 // Any other methods for functionality
}
top.customElements.define('x-a', XAElement)

Is it allowed to define custom elements of the top browsing context in an iframe?

Note: The file iframe.html is any html file that loads this script; and the file index.html (in which the error happens) is any file that loads the iframe iframe.html.

Note 2: The error I get is afer super(); however, if I comment the last line (in which I intend to define the custom element), no error happens.

  • You try to declare the custom element class in the iframe context but you define the custom element in the top container which is a different javascript context. Why don't you define the custom element in the iframe context? – Supersharp Feb 17 '19 at 16:31
  • @Supersharp, If I define the custom element in the iframe browsing context, the custom element does not work in the top browsing context. – Raúl Arturo Chávez Sarmiento Feb 17 '19 at 22:50

3 Answers3

2

After modifying the script several times for testing, I realized that HTMLElement was different from top.HTMLElement. That is why inhering from HTMLElement didn't work for defining a custom element since it is only allowed to inherit from the HTMLElement from the browsing context where the custom element is defined.

Then, modifying the script to:

// iframe.js

class XAElement extends top.HTMLElement{
 constructor(){
  super()
  // Any customization here
 }
 // Any other methods for functionality
}
top.customElements.define('x-a', XAElement)

it ends up working.

I'll go this way because, after defining the custom element and saving the required template contents in the constructor class (as properties, can be done before defining the custom element), I can delete the iframe. This way, I only have to load the iframe; wait for these requirements to be met; and delete the iframe to (I think) revert the performance impact of creating a nested browser content.

Note: If script is executed before the templates to be used are loaded, saving the template contents in the constructor should be put inside a load event listener.

Edition:

I forgot to test what happens if I delete the iframe at the moment of posting the answer. What happens is that it stops working; that is why I'm accepting the other answer.

1

If you want to mimic HTML Imports you'd better use the HTMLImports.min.js library provided in the Github repository.

<script src="htmlimports.min.js"></script>

Note that HTML Imports native support in Chrome will be removed in the next release (version 73) as no consensus were found with Mozilla and Apple on this feature proposed by Google... but the above library will still work as a simple HTML loader.


Alternately you can load HTML content with fetch() as described in this post about HTML Import alternatives.


If you still want to use an <iframe> element to load a HTML file, you'll need to wait for the file to be laoded before accessing its content.

<iframe src="XA.html" onload="defineXA()"></iframe>

I don't think it's a good practise because it will create useless browsing context.

Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • Ok, I think I'll go with this approach. But, before doing so, I'd like to understand why I cannot use a class defined in the iframe browsing context to define a custom element in the top browsing context. By the way, I planned to delete the iframe browsing context after it does everything it has to do; does it still affects performance? – Raúl Arturo Chávez Sarmiento Feb 17 '19 at 22:48
  • @RaúlArturoChávezSarmiento Because the Javascript code can only be executed in its context but I think you found it by yourself – Supersharp Feb 18 '19 at 07:59
0

Honestly I would avoid trying to load a Web Component using HTML imports. It is deprecated and going to be removed from all browsers.

Instead start using ES6 modules or even just create traditional JavaScript files.

There are plenty of ways to package your template into a JS file, including component-build-tools or Webpack or other things.

My advice is to stop using dead tech and convert to the current paradigms of module loading.

Intervalia
  • 10,248
  • 2
  • 30
  • 60