My website has a list of projects defined roughly as:

- Index.html
<body>
    <div class="projects">
        <div class="project">
            <span>Project Name</span>
            <video>Project Video</video>
        </div>
        <div class="project">
            <span>2nd Project Name</span>
            ...
    </div>
</body>

The simple way to solve this is by manually writing this template over and over for each project.

This isn't scalable however and a pain to update, so I want to encapsulate the HTML into a web-component. This would would result in an index page that looks something like this:

- Index.html
<head>
    <link rel="import" href="Project.html">
</head>
<body>
    <div class="projects">
        <project text="Example" video="example.mp4"></project>
        <project text="AnotherExample" video="example2.mp4"></project>
    </div>
</body>

You'll note the custom "project" tags being used along with non-standard properties being passed into them. This is a web-component, which I need to define in Project.html

Without any frameworks, a raw web-component would result in code looking something like this (shown in full mostly for effect):

- Project.html
<template>
    <div>
        <span></span>
        <video></video>
    </div>
</template>

<script>
    function(window, document, undefined) {

        var template = document.querySelector('template').content;

        var projectProto = Object.create(HTMLElement.prototype);

        projectProto.createdCallback = function() {

            var clone = document.importNode(template, true);

            var shadowRoot = this.createShadowRoot();
            shadowRoot.appendChild(clone);

            this.spanNode = shadowRoot.querySelector("span");
            this.videoNode = shadowRoot.querySelector("video");
        };

        projectProto.attributeChangedCallback = function(attr, oldVal, newVal) {
            if (attr == "text")
                this.spanNode.textContent = newVal;
            else if (attr == "video")
                this.videoNode.textContent = newVal;
        };

        window.project = document.registerElement('project', {
            prototype: projectProto
        });
    })(window, document);
</script>

This defines the web component in VanillaJS[1], along with two data-bindings to the internal tags that need to be filled in.

Binding values directly to values however is again an unscalable solution. A number of frameworks are emerging to try solve this problem by offering data-binding for the template.

The following is the same as above but now using Google's Polymer 1.0 framework.

- Project.html
<link rel="import" href="/bower_components/polymer/polymer.html">

<polymer-element name="project">
    <template>
        <style>
            :host {
                padding: 10px;
                border: 1px solid red;
            }
        </style>
        <div>
            <span>{{text}}</span>
            <video>{{video}}</video>
        </div>
    </template>

    <script>
        Polymer('project', {
            text: "Default",
            video: "default.mp4",
        });
    </script>
</polymer-element>

This is obviously much cleaner[1:1] than the pure web-components example because of the in-built data-binding.

I've also shown in the above how styling can be applied to a web-component. With the above structure, I can now update the project's style individually by updating this file rather than Index.html or a separate style-sheet.


  1. I saw the 2.0 implementation at the Polymer Summit (17th Oct) which is an even cleaner example than above given their move over to ES6 class-based style. ↩︎ ↩︎