Skip to content

Instantly share code, notes, and snippets.

@adamliptrot-oc
Last active November 22, 2022 11:16
Show Gist options
  • Save adamliptrot-oc/f7cbb92f040082cc17cff27416ae348b to your computer and use it in GitHub Desktop.
Save adamliptrot-oc/f7cbb92f040082cc17cff27416ae348b to your computer and use it in GitHub Desktop.
Loads and polyfills the autocomplete
// Note - updated to work with the HMRC Frontend implementation
// https://github.com/hmrc/play-frontend-hmrc#adding-accessible-autocomplete-css-and-javascript
if (typeof HMRCAccessibleAutocomplete != 'undefined' && document.querySelector('[data-module="hmrc-accessible-autocomplete"]') != null) {
var originalSelect = document.querySelector('[data-module="hmrc-accessible-autocomplete"]');
// load autocomplete - now handled by the HMRC component wrapper in Twirl
// accessibleAutocomplete.enhanceSelectElement({
// selectElement: originalSelect,
// showAllValues: true
// });
// =====================================================
// Polyfill autocomplete once loaded
// =====================================================
var checkForLoad = setInterval(checkForAutocompleteLoad, 50);
var parentForm = upTo(originalSelect, 'form');
function polyfillAutocomplete(){
var combo = parentForm.querySelector('[role="combobox"]');
// =====================================================
// Update autocomplete once loaded with fallback's aria attributes
// Ensures hint and error are read out before usage instructions
// =====================================================
if(originalSelect && originalSelect.getAttribute('aria-describedby') > ""){
if(parentForm){
if(combo){
combo.setAttribute('aria-describedby', originalSelect.getAttribute('aria-describedby') + ' ' + combo.getAttribute('aria-describedby'));
}
}
}
// =====================================================
// Update autocomplete once loaded with error styling if needed
// This won't work if the autocomplete css is loaded after the frontend library css because
// the autocomplete's border will override the error class's border (they are both the same specificity)
// but we can use the class assigned to build a more specific rule
// =====================================================
setErrorClass();
function setErrorClass(){
if(originalSelect && originalSelect.classList.contains("govuk-select--error")){
if(parentForm){
if(combo){
combo.classList.add("govuk-input--error");
// Also set up an event listener to check for changes to input so we know when to repeat the copy
combo.addEventListener('focus', function(){setErrorClass()});
combo.addEventListener('blur', function(){setErrorClass()});
combo.addEventListener('change', function(){setErrorClass()});
}
}
}
}
// =====================================================
// Ensure when user replaces valid answer with a non-valid answer, then valid answer is not retained
// =====================================================
var holdSubmit = true;
parentForm.addEventListener('submit', function(e){
if(holdSubmit){
e.preventDefault()
if(originalSelect.querySelectorAll('[selected]').length > 0 || originalSelect.value > ""){
var resetSelect = false;
if(originalSelect.value){
if(combo.value != originalSelect.querySelector('option[value="' + originalSelect.value +'"]').text){
resetSelect = true;
}
}
if(resetSelect){
originalSelect.value = "";
if(originalSelect.querySelectorAll('[selected]').length > 0){
originalSelect.querySelectorAll('[selected]')[0].removeAttribute('selected');
}
}
}
holdSubmit = false;
//parentForm.submit();
HTMLFormElement.prototype.submit.call(parentForm); // because submit buttons have id of "submit" which masks the form's natural form.submit() function
}
})
}
function checkForAutocompleteLoad(){
if(parentForm.querySelector('[role="combobox"]')){
clearInterval(checkForLoad)
polyfillAutocomplete();
}
}
}
// Find first ancestor of el with tagName
// or undefined if not found
function upTo(el, tagName) {
tagName = tagName.toLowerCase();
while (el && el.parentNode) {
el = el.parentNode;
if (el.tagName && el.tagName.toLowerCase() == tagName) {
return el;
}
}
// Many DOM methods return null if they don't
// find the element they are searching for
// It would be OK to omit the following and just
// return undefined
return null;
}
// Ensure the error styling is presented (powered by adding the govuk-input-error class in js)
// This can't be done just by adding the class due to conflicting specificity
.autocomplete__input.govuk-input--error {
border-color: #d4351c;
}
// Ensure the down arrow for 'show all' option sits above the input so it is visible
// (there was a change - possibly either input or page background which effectively hid the arrow due to its z-index)
.autocomplete__dropdown-arrow-down {
z-index: 0;
pointer-events: none;
}
// Constrain the input (and drop-down) so they site with the other address inputs
// important to do this on the wrapper rather than the input itself otherwise the down-arrow will be floating by itself
@media (min-width: 40.0625em){
.autocomplete__wrapper {
width: 75%!important;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment