diff --git a/js/formidable.js b/js/formidable.js index 41c018a4bd..0df9355e45 100644 --- a/js/formidable.js +++ b/js/formidable.js @@ -1154,6 +1154,24 @@ function frmFrontFormJS() { return kvp.join( '&' ); } + /** + * Inserts error HTML into a field's container, tagging every inserted top-level + * element with a data-frm-error attribute. removeFieldError()/removeAllErrors() rely + * on that attribute (rather than the frm_error class) to find and remove it again, + * since a site's own custom field HTML template can render the [error] placeholder + * without a frm_error class or id, and errors that can't be found never get removed. + * + * @param {HTMLElement} container + * @param {string} errorHtml + * @return {void} + */ + function insertErrorHtml( container, errorHtml ) { + const template = document.createElement( 'template' ); + template.innerHTML = errorHtml; + Array.from( template.content.children ).forEach( el => el.setAttribute( 'data-frm-error', '' ) ); + container.append( template.content ); + } + function addFieldError( $fieldCont, key, jsErrors ) { const container = $fieldCont instanceof jQuery ? $fieldCont.get( 0 ) : $fieldCont; @@ -1177,7 +1195,7 @@ function frmFrontFormJS() { const roleString = frm_js.include_alert_role ? 'role="alert"' : ''; errorHtml = `
${ jsErrors[ key ] }
`; } - container.insertAdjacentHTML( 'beforeend', errorHtml ); + insertErrorHtml( container, errorHtml ); inputs.forEach( input => { describedBy = input.getAttribute( 'aria-describedby' ); if ( ! describedBy ) { @@ -1236,7 +1254,7 @@ function frmFrontFormJS() { return; } - const errorMessage = container.querySelector( '.frm_error' ); + const errorMessages = container.querySelectorAll( '.frm_error, [data-frm-error]' ); const input = container.querySelector( 'input, select, textarea' ); container.classList.remove( 'frm_blank_field', 'has-error' ); @@ -1252,10 +1270,10 @@ function frmFrontFormJS() { } } - if ( errorMessage ) { + errorMessages.forEach( errorMessage => { removeElementFromInputDescribedBy( errorMessage ); errorMessage.remove(); - } + } ); } /** @@ -1286,7 +1304,7 @@ function frmFrontFormJS() { document.querySelectorAll( '.form-field' ).forEach( field => { field.classList.remove( 'frm_blank_field', 'has-error' ); } ); - document.querySelectorAll( '.form-field .frm_error' ).forEach( el => { + document.querySelectorAll( '.form-field .frm_error, .form-field [data-frm-error]' ).forEach( el => { removeElementFromInputDescribedBy( el ); el.remove(); } ); @@ -1438,7 +1456,7 @@ function frmFrontFormJS() { return; } - const errors = document.querySelectorAll( '.frm_form_field .frm_error' ); + const errors = document.querySelectorAll( '.frm_form_field .frm_error, .frm_form_field [data-frm-error]' ); if ( ! errors.length ) { return; }