Last active
June 16, 2023 13:34
-
-
Save derekcavaliero/1d16bc7f02357087e179aa19f8c7ce08 to your computer and use it in GitHub Desktop.
HubSpot Embedded Form Accessibility Polyfills
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* HubSpot Embedded Form Accessibility Pollyfills | |
* | |
* This script fixes the HubSpot HTML blunders that make their embedded forms inaccessible for assistive technology. | |
* - Replaces/removes improper use of <fieldset>, <legend>, <label>, and role attributes. | |
* - Note - this can cause forms configured for mulitple column field layouts to break - you will need to adjust your CSS accordingly. | |
**/ | |
hubspotFormA11y = { | |
changeTag: function(node, tag) { | |
const clone = document.createElement(tag); | |
for (const attr of node.attributes) { | |
clone.setAttributeNS(null, attr.name, attr.value); | |
} | |
while (node.firstChild) { | |
clone.appendChild(node.firstChild); | |
} | |
node.replaceWith(clone); | |
return clone; | |
}, | |
polyfillInitialHtml: function(form) { | |
form.querySelectorAll('fieldset, legend').forEach(element => { | |
this.changeTag(element, 'div'); | |
}); | |
form.querySelectorAll('.hs-fieldtype-checkbox, .hs-fieldtype-radio').forEach(element => { | |
element.querySelectorAll('[role]').forEach(item => { | |
item.removeAttribute('role'); | |
}); | |
this.changeTag(element, 'fieldset'); | |
}); | |
form.querySelectorAll('.hs-fieldtype-checkbox > label, .hs-fieldtype-radio > label').forEach(element => { | |
this.changeTag(element, 'legend'); | |
}); | |
}, | |
polyfillErrorMessages: function(fieldRoot) { | |
let errorRoot = fieldRoot.querySelector('.hs-error-msgs'); | |
let errors = errorRoot.querySelectorAll('.hs-error-msg'); | |
errorRoot.removeAttribute('role'); | |
let input = fieldRoot.querySelector('.hs-input'); | |
errorRoot.id = input.name + '-error-msgs'; | |
input.setAttribute('aria-describedby', input.name + '-error-msgs'); | |
errors.forEach(error => { | |
let label = fieldRoot.querySelector('label[for="' + input.id + '"]'); | |
if (error.innerText == 'Please complete this required field.') | |
error.innerText = 'The "' + label.innerText.replace('*', '') + '" field is required.'; | |
this.changeTag(error, 'span'); | |
}); | |
}, | |
untetherErrorMessages: function(fieldRoot) { | |
let input = fieldRoot.querySelector('.hs-input'); | |
input.removeAttribute('aria-describedby'); | |
}, | |
observer: function(form) { | |
const observer = new MutationObserver((mutationList, observer) => { | |
for (const mutation of mutationList) { | |
if (mutation.type !== 'childList') | |
return; | |
if (mutation.addedNodes.length) { | |
let node = mutation.addedNodes[0]; | |
if (node.nodeType === 3) // text node early return | |
return; | |
// console.log('Node(s) added', mutation.addedNodes, mutation.target); | |
if (node.matches('.hs-error-msgs')) | |
this.polyfillErrorMessages(mutation.target); | |
} | |
if (mutation.removedNodes.length) { | |
let node = mutation.removedNodes[0]; | |
if (node.nodeType === 3) // text node early return | |
return; | |
// console.log('Node(s) removed', mutation.removedNodes, mutation.target); | |
if (node.hasOwnProperty('matches') && !node.matches('.hs-error-msgs')) | |
return; | |
this.untetherErrorMessages(mutation.target); | |
} | |
} | |
}); | |
observer.observe(form, { | |
attributes: false, | |
childList: true, | |
subtree: true | |
}); | |
} | |
}; | |
window.addEventListener('message', msg => { | |
if (msg.data.type !== 'hsFormCallback') | |
return; | |
const formId = msg.data.id; | |
const form = document.querySelector('form[data-form-id="' + formId + '"]'); | |
switch(msg.data.eventName) { | |
case 'onFormReady': | |
hubspotFormA11y.polyfillInitialHtml(form); | |
hubspotFormA11y.observer(form); | |
break; | |
case 'onFormFailedValidation': | |
break; | |
case 'onFormSubmit': | |
break; | |
case 'onFormSubmitted': | |
break; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment