Dynamic Class Bootstrapping

by Simon Harte

HTML

<div class="js-linkComponent" data-newTab>
    <span class="js-linkTarget" data-link="link1"></span>
</div>
<div class="js-linkComponent">
    <span class="js-linkTarget" data-link="link2"></span>
</div>
<div class="js-linkComponent">
    <span class="js-linkTarget" data-link="link3"></span>
</div>
<div class="js-linkComponent" data-newTab>
    <span class="js-linkTarget" data-link="link4"></span>
</div>

JavaScript

// Base Component Class
class Component {
    static get defaults() {
        return {
            errors: {
                noCtx: 'no context node found',
            }
        }
    }

    constructor(context) {
        if (!context) {
            console.error(`${this.constructor.name}: ${Component.defaults.errors.noCtx}`);
            return;
        }

        this.context = context;
    }

    collectElements(selectors) {
        const elements = {};

        for (let [key, selector] of Object.entries(selectors)) {
            const nodes = this.selectInContext(selector);

            elements[key] = nodes.length > 1 ? nodes : nodes[0];
        }

        return elements;
    }

    selectInContext(selector, all = true) {
        const method = all ? 'querySelectorAll' : 'querySelector';

        return this.context[method](selector);
    }
}

// Link Component File
class Link extends Component {
    static get defaults() {
        return {
            ...super.defaults,
            ...{
                _ctx: '.js-linkComponent',
                selectors: {
                    link: '.js-linkTarget',
                },
            },
        };
    }

    constructor(context) {
        super(context);

        this.elements = this.collectElements(Link.defaults.selectors);

        this.settings = {
            openNewTab: context.hasAttribute('data-newTab'),
        }
    }

    init() {
        this.elements.link.addEventListener('click', this.onClickLink.bind(this));

        this.populateElements();
    }

    onClickLink(ev) {
        const url = ev.target.getAttribute('data-link');

        this.openLink(url);
    }

    openLink(url) {
        if (this.settings.openNewTab) {
            window.open(url);
        } else {
            window.location = url;
        }
    }

    populateElements() {
        const target = this.elements.link;
        const text = document.createTextNode(`open ${target.dataset.link} in new tab:...