From 7dd400bd1bb6ba23cdd9c05cf822a98877124aeb Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Tue, 15 Sep 2026 12:28:37 -0300 Subject: [PATCH 1/2] Fix pro issue 6392 --- js/formidable.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/js/formidable.js b/js/formidable.js index 41c018a4bd..97db469a18 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 errorMessage = container.querySelector( '.frm_error, [data-frm-error]' ); const input = container.querySelector( 'input, select, textarea' ); container.classList.remove( 'frm_blank_field', 'has-error' ); @@ -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; } From e3a4ba9c54c10047f5add163211e54e8dbe42b03 Mon Sep 17 00:00:00 2001 From: "vivi-the-going-merry[bot]" <308115520+vivi-the-going-merry[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 13:27:53 -0600 Subject: [PATCH 2/2] Remove all tagged error elements during field revalidation removeFieldError() only removed the first [data-frm-error] match, but insertErrorHtml() tags every top-level element in custom error markup. Multi-element custom error templates left stale error content visible after the field was corrected. Switch to querySelectorAll + forEach, matching the existing removeAllErrors() pattern. Requested by Franky/CodeRabbit review on this PR. --- js/formidable.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/formidable.js b/js/formidable.js index 97db469a18..0df9355e45 100644 --- a/js/formidable.js +++ b/js/formidable.js @@ -1254,7 +1254,7 @@ function frmFrontFormJS() { return; } - const errorMessage = container.querySelector( '.frm_error, [data-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' ); @@ -1270,10 +1270,10 @@ function frmFrontFormJS() { } } - if ( errorMessage ) { + errorMessages.forEach( errorMessage => { removeElementFromInputDescribedBy( errorMessage ); errorMessage.remove(); - } + } ); } /**