Cookbook Home ~ alanwsmith.com ~ links ~ podcast ~ twitter

Using Mutation Observers To Setup Communication Between Web Components And Their Parent Page In Vanilla JS

Deatils

Example

Web Component

Parent Page

HTML

<p>Web Component</p>
<com-link id="the-link"></com-link>

<p>Parent Page</p>
<input id="outside-slider" type="range" />

JavaScript Web Component

class ComLink extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({ mode: 'open' })

        const handleInsideUpdate = (event) => {
            this.setAttribute('amount', event.target.value)
        }

        this.rangeSlider = document.createElement('input')
        this.rangeSlider.addEventListener('input', handleInsideUpdate)
        this.rangeSlider.type = 'range'

        this.shadowRoot.append(this.rangeSlider)
    }

    static get observedAttributes() {
        return ['amount']
    }

    attributeChangedCallback(attribute, oldValue, newValue) {
        if (newValue !== oldValue) {
            this.setAttribute('amount', newValue)
            this.rangeSlider.value = newValue
        }
    }
}

customElements.define('com-link', ComLink)

JavaScript Main Page

const handleInsideMutations = (mutationList, observer) => {
    for (const mutation of mutationList) {
        // if (mutation.type === 'attributes') {
        // Make a note about skipping the 'if' here for attirbute
        // type check since you're only watching for attribute changes
        if (mutation.attributeName === 'amount') {
            const oldValue = mutation.oldValue
            const newValue = mutation.target.attributes.getNamedItem(
                mutation.attributeName
            ).value
            if (newValue !== oldValue) {
                document.getElementById('outside-slider').value = newValue
            }
        }
        // }
    }
}

const handleOutsideInput = (event) => {
    const newValue = event.target.value
    document.getElementById('the-link').setAttribute('amount', newValue)
}

const init = () => {
    document
        .getElementById('outside-slider')
        .addEventListener('input', handleOutsideInput)

    const theLink = document.getElementById('the-link')
    const observer = new MutationObserver(handleInsideMutations)
    observer.observe(theLink, { attributes: true })
}

document.addEventListener('DOMContentLoaded', init)

Notes

TODO

References