Using Mutation Observers To Setup Communication Between Web Components And Their Parent Page In Vanilla JS
Deatils
- This is a basic example showing how to setup bi-directoinal communiation between a web component and elements on the parent page in vanilla js
- The top input range slider in the example is from the web component. The bottom one lives on the parent page directly. Moving one moves the other by passing a value through an 'amount' attribute on the component
- The code for the example is directly below it. More notes follow the snippets
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
- First off. Yes. That's a long ass title for a page, but there's not many words to drop and get the point across
- This is vanilla js stuff. I'm not religious about that. Just exploring
- I expect there are other ways to do this. If you've got alternaties, please hit me up on twitter
TODO
- Do a full write up of the code (I made it as clear as I could, but there's still a lot and the process is convoluted)