DEV Community

TJ Fogarty
TJ Fogarty

Posted on • Originally published at tj.ie

HTML Imports & Component-Driven Development

This was originally posted on my blog.

I was thinking about the process of building a site today vs maybe 8 years ago. When WordPress was called for, I'd usually build the site from start to finish within WordPress. You wouldn't see any CSS for a little while as custom post types needed creating, content needed to be sourced, and plugins needed to be installed. The whole front-end was mashed up with this configuration, and nothing was really finished until the site was live.

Now, I didn't know a whole lot 8 years ago, so looking back there are ways it could have been done better. If I could go back, I'd probably build a static site first with all the parts, then port that over to WordPress when it was ready. It might be me, but I find not having to think about a CMS while doing initial front-end work very liberating. I don't want to troubleshoot why a template is breaking when I want to focus on styling. It's too easy for me to go off on a tangent and fiddle with PHP because I just thought of a better way to do something.

Years later, this idea of focusing on the right things at the right time became, for me at least, encapsulated with Atomic Design, so I want to take this moment to thank Brad Frost for preserving the few remaining brain cells I have left. I used tools like Pattern Lab, and Fractal which really opened my eyes to the benefits of focusing on one thing at a time.

Working on a team and incorporating these tools caused trouble at times. When I wanted to introduce this notion of building a project from components, everyone had to be on the same page, and have a solid understanding of the tools we were using. Technical bugs arose from misconfigured environments or an unmet expectation, and when you're moving fast with multiple projects, it can be a momentum-killer.

In many cases, I see nothing wrong with setting up a local PHP server in a directory with php -S localhost:8080, and setting up individual files for components and using include to pull them into a template. Not all projects demand a batteries-included pattern library that can be exhibited to a client. Sometimes they don't care as long as the project gets done on time and within budget. It'd be nice sometimes to surprise a client with such a powerful resource packaged up in Pattern Lab, but when it's not called for we still get that nice component-y feeling from a smattering of PHP calls to other files. The developer still gets to focus on one thing at a time.

HTML Imports

I was wondering if there's any way we can replicate this in the browser without relying on external libraries. Maybe it's something that could be done with the tools we have today without depending too much on JavaScript to make a bunch of AJAX calls. That's when I was reminded of HTML Imports. I wonder how far along that is now...

HTML Import support

Well, at the moment it's not great. Mozilla have published their thoughts on supporting it.

At Mozilla we want to explore how importing custom element definitions can align with upcoming ES6 module APIs. Weโ€™d be prepared to implement if/when they appear to enable developers to do stuff they canโ€™t already do.

Chrome will be removing the current implementation as well.

Still, though, I wanted to try to replicate that process of having components and including them where I needed them, without requiring a developer to know the ins-and-outs so they can focus on coding.

Importing HTML

Given I have an index.html, a folder for partials (think a header and a footer which may be made up of components), and a folder for components, I want to load them, and inject them into the page. The first thing to do is register them in the <head> of my document with <link rel="import">.

<link rel="import" id="site-header" href="partials/site-header.html">
<link rel="import" id="primary-nav" href="components/navigation/primary-nav.html">
Enter fullscreen mode Exit fullscreen mode

The ID is important for referencing the imports in order to inject them into the page. So how do we reference those imports? I'm going to use a data attribute to define this.

<div data-import="site-header"></div>
Enter fullscreen mode Exit fullscreen mode

Inside my site header, I'm also referencing the primary nav.

<header>
  <div>
    <span>Logo</span>
    <div data-import="primary-nav"></div>
  </div>
</header>
Enter fullscreen mode Exit fullscreen mode

JavaScript

We need some JavaScript to get the ball rolling. We need to find the imports, grab their content, and replace instances of [data-import] with their respective content. To be fair, it's not an awful lot of code, which was a nice surprise.

  1. Get the import id to identify where to import into our document later on
  2. Grab the contents of the import. Since it'll include the body tag, we want to get everything inside that instead.
  3. Find the spots to put it in.
  4. Loop through each data-import and replace it with the contents.

For step 4, I'm using content.clondNode(true) rather than passing content. I'm doing this because if there's more than one instance on the page, it'll only show the import in the last place it was referenced, essentially moving it around. By using cloneDeep, and passing true to include the children, we're copying it into every place it's being referenced.

let imports = document.querySelectorAll('link[rel="import"]')

imports.forEach(htmlImport => {
  let id = htmlImport.getAttribute('id'), // [1]
      content = htmlImport.import.querySelector('body *'), // [2]
      domTemplate = document.querySelectorAll(`[data-import="${id}"]`) // [3]

  domTemplate.forEach(el => {
    el.parentNode.replaceChild(content.cloneNode(true), el) // [4]
  })
});
Enter fullscreen mode Exit fullscreen mode

I thought this was a fun little experiment, and it would be really cool to see this factor into the tooling of creating websites in the future. That's if the support is there. Otherwise, there are custom elements to look into, or I'm happy sticking with a static-like setup with PHP.

See the demo
View the source

Top comments (12)

Collapse
 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ

As discussed, HTML imports have been deprecated in favor of JavaScript modules. New apps should not use them.

There's an early proposal for HTML modules which might interest you.

github.com/w3c/webcomponents/issue...

Collapse
 
teej profile image
TJ Fogarty

Thanks, Benny. I suppose what I was getting at is exploring a potential replacement for depending on a server or build tools for this kind of stuff, which mimicked an older process I had. HTML Imports came closest to what I was looking for.

I'll read up more on this for sure, thanks for the link!

Collapse
 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ

There's no need for a server or build tools when using javascript modules

<script type="module">
  import { makeDom } from './my-dom-module.js'
  const someDom = makeDom();
  document.body.append(someDom);
</script>

Easy peasy, lemon squeezy!

Thread Thread
 
teej profile image
TJ Fogarty

Ah ok, cool! Pardon my ignorance here, but how do I get a HTML partial out of the JavaScript module? Can I import HTML?

Thread Thread
 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ

Great question, one that's inspired me to write a post on web components standards (watch this space!)

You could do something like

// partial.js
const template = document.createElement('template');

template.innerHTML = `
<div>
  <p>Here's my reusable partial</p>
  <button>OK</button>
</div>
`;

export function partial() {
  return template.content.cloneNode(true)
}

And elsewhere...

import { partial } from './partial.js';
document.getElementById('container').append(partial())

Take a look at the lit-html library for a functional way of building UI with template literals

import { html } from 'lit-html'

const messageBox = message =>
  html`<span class="message-box">${message}</span>`;

const showMessage = message =>
  render(messageBox(message), document.getElementById('container'))
Thread Thread
 
teej profile image
TJ Fogarty

Thanks for taking the time to give an explanation, looking forward to your post!

My experiment was to see if I could avoid complicating things with JavaScript, and reduce the barrier to entry for a developer working on styling up the front-end. I'll be interested in seeing how this space grows.

Thread Thread
 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ

Yeah, that's exactly what HTML imports were designed for. The loss of that API was a pretty difficult pill to swallow, but the tradeoff is that web components are now standard across almost all browsers.

Collapse
 
yucer profile image
yucer

Ohh, I think the next October 23th will be the 20th anniversary that Microsoft has proposed this to W3C under the name of HTML Components. It was available since IE 5.5.

I have tested it in the year 2000, creating an HTML component for the selection of date & time values (Calendar). The nice stuff is that you could also easily associate "events" to this components and reuse them easily in many pages and HTML tags, this way.

The problem is that the other browsers have not implemented it. The same might have happened with other standards like HTML+TIME. One drawback of standards is that everybody should implement them, otherwise they don't have success.

My guess is that the javascript community was in one stage where they wanted to have more freedom to implement such stuff in different ways. And maybe the lack of such implementations in the browser gave rise to the incredible amounts of javascript frameworks that exist nowadays. I like how the javascript components are implemented in Odoo

It is a kind of a trade-off. If you implement more in the browsers, they become complex (and hence difficult to implement) and there is less competition in the javascript frameworks. Maybe this aggressive evolution of Internet Explorer was the cause of its complexity, the amount of security issues and other negative stuffs in its story.

After that, I think Microsoft did put more efforts defining the components in the server side in order not having to deal with those delays in browser implementations. ASP.NET allows you to group the HTML, CSS and Javascript in an extensible unit called ASP.NET Control and reuse it many times. The engine make sometimes optimization according to the client browser.

I think you are right. Re-usability is the key word for the success, and component-ware is good way to achieve it.

Congratulations! Not every developer has the vision to look out-of-the-box. ;-)

Collapse
 
teej profile image
TJ Fogarty

Thank you, and great comment! Loads of reading material in there to dig in to.

Collapse
 
fnh profile image
Fabian Holzer • Edited

HTML imports have been deprecated, and they'll be removed from Chrome within less than a year. For packaging web components it seems that ES6 modules will become the mainstream approach.

Collapse
 
teej profile image
TJ Fogarty

Thanks, Fabian! I've added a link for that thread.

Collapse
 
alephnaught2tog profile image
Max Cerrina

I've done a decent amount with React and so am pretty familiar with components -- and they, and the other JS tools I've used similarly including "normal" browser-based script modules -- are just never what I want. Last year I did a ton with PHP and also JSP in school and side projects, and then work was very React-heavy; this current project, however, is Phoenix-based, which means I am back in the land of imports and includes and ... man, the JS-based stuff just doesn't hold a candle to it. I've rigged up some neat stuff combining React and traditionally-done server-side stuff like Phoenix etc and done some neat stuff with both (and, IMHO, they're a fabulous compliment to each other, as so much of the maddening "I just want a goddamn link to another page, this shouldn't be a huge issue" that is part of the single-page-app-ness is gone)

I totally get why you wanted to see what you could rig up yourself. I really prefer that style as well.