Skip to content

Todo para WordPress, el desarrollo web — y mucho más

🐛 Contact Form 7 en popup de Elementor: por qué el formulario recarga la página y cómo solucionarlo

🐛 Contact Form 7 en popup de Elementor: por qué el formulario recarga la página y cómo solucionarlo

Usted añade un formulario de contacto mediante Contact Form 7 dentro de un popup de Elementor Pro. El usuario completa los campos, hace clic en «Enviar» y la página entera se recarga.

Sin mensajes de error. Sin confirmación de envío. Solo una recarga y un lead perdido.

Este es un conflicto conocido: CF7 depende del envío mediante AJAX, pero dentro de un popup cargado dinámicamente su JavaScript no tiene tiempo de vincularse al formulario. Como resultado, el navegador realiza un envío HTML estándar, el mismo que provoca la recarga.

El problema existe desde hace años, pero se soluciona con solo dos líneas de código. A continuación se presentan dos soluciones funcionales: una moderna (más limpia y fiable) y una alternativa de GitHub, además de mejoras opcionales y una lista de verificación para depuración.

💡 Resumen rápido:

  • Causa raíz: CF7 se inicializa al cargar la página, pero el popup con el formulario aparece después, por lo que el script no sabe nada de su contenido
  • Solución 1: reinicializar CF7 cuando se dispara el evento elementor/popup/show; JavaScript espera a que el popup se abra y detecta el formulario
  • Solución 2: rastrear los clics en el botón que abre el popup con un retardo para la animación; este método proviene de una discusión en GitHub sobre Elementor
  • Opcional: restablecer el formulario al reabrir y cerrar automáticamente el popup tras un envío exitoso
  • Lista de verificación para depuración: consola del navegador, conflictos con jQuery, plugins de caché

Por qué CF7 falla específicamente en un popup

Programador arreglando un fallo de Contact Form 7 en WordPress

Contact Form 7 está construido sobre AJAX: el formulario se envía sin recargar, la validación de campos ocurre sobre la marcha y los mensajes de error o éxito aparecen al instante. Sin embargo, todo este mecanismo se vincula al DOM al cargar la página mediante una llamada a wpcf7.init().

Elementor Pro carga el contenido del popup de forma dinámica, después del evento DOMContentLoaded. Cuando un usuario hace clic en un botón y el popup se abre, su HTML se inserta en el documento, pero CF7 no sabe nada de él. El formulario dentro del popup permanece sin inicializar.

Lo que sucede a continuación: sin un manejador AJAX activo, el navegador realiza un envío de formulario HTML estándar. El atributo action se dispara y la página se recarga. El popup se cierra de forma natural (su estado se restablece al navegar). El usuario ve una recarga y se va.

Muchos desarrolladores intentan tratar los síntomas: bloquean el cierre del popup mediante event.stopPropagation(), sobrescriben funciones internas de Elementor o impiden el envío del formulario con e.preventDefault(). Ninguno de estos métodos aborda la causa raíz (la falta de inicialización de CF7). Algunos incluso rompen las animaciones del popup o el propio Elementor en todo el sitio.

Solo hay una solución fiable: esperar a que el popup se abra y llamar explícitamente a wpcf7.init() para cada formulario en su interior.

Solución 1: reinicialización al abrir el popup (enfoque moderno)

Este método utiliza el evento nativo elementor/popup/show de Elementor. Coloque el código en el archivo functions.php de su tema activo o añádalo mediante un plugin de fragmentos como Code Snippets.

Cree una copia de seguridad de functions.php antes de editarlo.

1/**
2 * Reinitialize Contact Form 7 when opening an Elementor popup.
3 * Fixes page reload after form submission.
4 */
5function sdstudio_cf7_reinit_in_popup() {
6 ?>
7 <script>
8 jQuery( document ).on( 'elementor/popup/show', function() {
9 document.querySelectorAll( '.wpcf7 form' ).forEach( function( form ) {
10 if ( typeof wpcf7 !== 'undefined' ) {
11 wpcf7.init( form );
12 }
13 });
14 });
15 </script>
16 <?php
17}
18add_action( 'wp_footer', 'sdstudio_cf7_reinit_in_popup' );

Después de guardar, abra el popup con el formulario y pruébelo: complete incorrectamente los campos obligatorios y haga clic en «Enviar». Los mensajes de validación deberían aparecer al instante sin recargar. Luego envíe el formulario correctamente y confirme que el mensaje de éxito también se muestra dentro del popup.

El código espera el evento elementor/popup/show, que está garantizado que se dispara después de que el contenido del popup se haya renderizado. Luego querySelectorAll encuentra todos los formularios de CF7 en el DOM actual, y wpcf7.init() adjunta forzosamente la validación y el envío AJAX a cada uno. La comprobación typeof wpcf7 !== 'undefined' protege contra errores si CF7 no se ha cargado por alguna razón.

Solución 2: rastreo de clics en el botón del popup (método alternativo)

Este enfoque se publicó en la discusión de GitHub de Elementor #7798 por el usuario @drinkmaker. En lugar de rastrear la apertura del popup, monitoriza los clics en un botón o enlace con href='#elementor-action', que es exactamente como Elementor activa los popups.

El retardo setTimeout(..., 800) da tiempo a la animación de aparición del popup antes de que el código encuentre e inicialice el formulario. El marcador .elementor evita reinicializar el mismo formulario dos veces.

1/**
2 * Alternative initialization of CF7 in Elementor popups.
3 * Source: https://github.com/elementor/elementor/issues/7798 (drinkmaker)
4 */
5function sdstudio_elementor_cf7_alt_init() {
6 ?>
7 <script type='text/javascript'>
8 jQuery( document ).ready( function() {
9
10 jQuery( document ).on( 'click', "a[href='#elementor-action']", function() {
11
12 setTimeout( function() {
13
14 jQuery( '.elementor-popup-modal form.wpcf7-form:not(.elementor)' ).each( function( index ) {
15 wpcf7.initForm( jQuery( this ) );
16 jQuery( this ).addClass( 'elementor' );
17 });
18
19 }, 800 );
20
21 });
22
23 });
24 </script>
25 <?php
26}
27add_action( 'wp_footer', 'sdstudio_elementor_cf7_alt_init' );

Qué método elegir: el primero (que usa el evento elementor/popup/show) es preferible porque se basa en la API documentada de Elementor, es más limpio y no depende de temporizadores. El segundo ha sido probado en producción durante años y sirve como un Plan B fiable si el primero no funciona por alguna razón.

Opcional: restablecer el formulario al reabrir

Cuando un usuario cierra el popup y lo abre de nuevo, los campos del formulario permanecen completados. Esto resulta confuso: no queda claro si el formulario se envió o no. Una pequeña adición a la primera solución lo corrige:

1/**
2 * Reset CF7 form each time an Elementor popup opens.
3 */
4function sdstudio_cf7_reset_on_popup_open() {
5 ?>
6 <script>
7 jQuery( document ).on( 'elementor/popup/show', function() {
8 jQuery( '.wpcf7 form' ).each( function() {
9 this.reset();
10 jQuery( this ).find( '.wpcf7-not-valid' ).removeClass( 'wpcf7-not-valid' );
11 jQuery( this ).find( '.wpcf7-response-output' ).hide();
12 jQuery( this ).find( '.wpcf7-not-valid-tip' ).remove();
13 });
14 });
15 </script>
16 <?php
17}
18add_action( 'wp_footer', 'sdstudio_cf7_reset_on_popup_open' );

La función restablece los valores de los campos con reset(), elimina las clases CSS de los campos no válidos, oculta los mensajes de envío y elimina los avisos de validación. El usuario siempre ve un formulario limpio.

Opcional: cerrar el popup después de un envío exitoso

Después de un envío exitoso, tiene sentido cerrar automáticamente el popup tras 1,5 segundos para que el usuario tenga tiempo de leer la confirmación sin tener que buscar el botón de cierre:

1/**
2 * Auto-close Elementor popup after successful CF7 submission.
3 */
4function sdstudio_close_popup_on_cf7_success() {
5 ?>
6 <script>
7 document.addEventListener( 'wpcf7mailsent', function() {
8 setTimeout( function() {
9 jQuery( '.dialog-close-button' ).trigger( 'click' );
10 }, 1500 );
11 }, false );
12 </script>
13 <?php
14}
15add_action( 'wp_footer', 'sdstudio_close_popup_on_cf7_success' );

El evento wpcf7mailsent se dispara cuando el servidor confirma que el correo electrónico se ha enviado. El retardo de 1500 ms da al usuario tiempo para leer el mensaje de éxito. Hacer clic en .dialog-close-button utiliza el botón de cierre estándar de Elementor. A diferencia de los intentos de llamar directamente a la API del popup, este método es estable en todas las versiones.

Lista de verificación para depuración

Si el formulario aún recarga la página después de añadir el código, siga estos pasos:

  • Consola del navegador. Abra DevTools (F12 → Consola) y busque errores de JavaScript en rojo. Una causa común es que jQuery no se cargue o entre en conflicto con otro plugin.

  • Caché. Plugins como WP Rocket, Autoptimize o la caché a nivel de servidor pueden minificar y combinar scripts. Desactive temporalmente la optimización agresiva de JS y pruebe de nuevo.

  • jQuery en modo noConflict. Si un tema o plugin envuelve jQuery en noConflict, reemplace jQuery por $ usando un envoltorio apropiado, o use la forma completa jQuery.

  • ID del popup. Asegúrese de que el formulario esté dentro del popup exacto activado por un botón con href='#elementor-action'. Para popups abiertos mediante otros tipos de activación (por ejemplo, con un temporizador), el primer método con elementor/popup/show es más fiable.

  • Conflicto de plugins. Desactive otros plugins uno por uno y pruebe, especialmente aquellos que añaden sus propios scripts de validación o modifican el comportamiento de los formularios.

  • Versión de CF7. Estas soluciones se han probado en Contact Form 7 versión 5.7+ y Elementor Pro 3.5+. Si su versión de CF7 es inferior a 5.7, la función wpcf7.init() puede tener un nombre diferente; actualice el plugin.

⁉️🤔 Preguntas frecuentes

¿Por qué CF7 funciona en una página normal pero falla en un popup?

Cuando una página normal se carga, el DOM ya está construido y CF7 tiene tiempo de inicializar todos los formularios. El popup de Elementor carga contenido de forma asíncrona, después de que CF7 haya terminado su trabajo. El formulario termina en el DOM pero sin un manejador JavaScript vinculado. Por eso comprobar que «funciona en una página aparte» no ayuda: las condiciones de carga son fundamentalmente diferentes. La solución siempre es la reinicialización forzada cuando el popup se abre, independientemente de si el formulario funciona en otro lugar.

¿Puedo arreglarlo sin código, usando un plugin o un ajuste?

No existe un plugin ya hecho que le permita «marcar una casilla y que simplemente funcione» para este error. El problema se sitúa en la intersección de dos productos independientes (Elementor y CF7), y cada uno funciona correctamente por sí solo. Complementos de terceros como WPB Popup for Contact Form 7 resuelven el problema de manera diferente: crean sus propios popups en lugar de arreglar Elementor. El código anterior es la intervención mínima necesaria. Lo añade una vez a functions.php y no requiere actualizaciones cuando salgan nuevas versiones de CF7 o Elementor.

El primer método no funcionó. ¿Qué debo comprobar antes de cambiar al segundo?

Compruebe tres cosas. Primero: el evento elementor/popup/show está disponible a partir de Elementor Pro 2.7; si su versión es inferior, use el segundo método directamente. Segundo: abra la consola y escriba typeof wpcf7; si es undefined, el plugin CF7 no ha cargado su JavaScript (busque errores o conflictos). Tercero: asegúrese de que el formulario dentro del popup tenga la clase .wpcf7; sin ella, el selector querySelectorAll('.wpcf7 form') no encontrará nada. En la gran mayoría de los casos, el primer método funciona de inmediato. Si no, use el segundo: ha sido probado en cientos de sitios a lo largo de los años.

¿Necesito añadir los tres fragmentos o basta con uno?

El primer fragmento (reinicialización) es el mínimo esencial. El segundo es una alternativa; añádalo solo si el primero no resolvió el problema. El restablecimiento del formulario y el cierre automático del popup son mejoras opcionales que puede añadir según necesite: el restablecimiento es útil si el popup se abre varias veces durante una misma visita; el cierre automático es útil si el popup se usa para consultas y no contiene un texto de confirmación extenso. Los tres fragmentos son independientes y pueden funcionar simultáneamente. No hay conflictos entre ellos.

Después de la corrección, el formulario se envía, pero los correos no llegan. ¿Está relacionado?

No, los problemas de entrega de correo electrónico son un tema aparte, no relacionado con el funcionamiento de CF7 dentro de un popup. Si después de aplicar la corrección el formulario muestra un mensaje de éxito (borde verde), el envío AJAX está funcionando correctamente. Correos que no llegan: compruebe la configuración SMTP, los filtros de spam del alojamiento y la dirección del destinatario en los ajustes del formulario de CF7. Para una entrega fiable, use un plugin SMTP como Post SMTP o FluentSMTP en lugar de la función estándar wp_mail(), ya que los servidores de alojamiento a menudo bloquean el correo PHP saliente.

¿Afecta esta solución a otros formularios de CF7 en el sitio?

No. Ambos métodos están aislados: el primero espera a que se abra un popup de Elementor, el segundo solo rastrea enlaces con href='#elementor-action'. Los formularios de CF7 normales colocados en páginas y widgets siguen funcionando con normalidad; se inicializan al cargar la página y no se ven afectados. La única salvedad: si su sitio usa caché agresiva con concatenación de scripts, añada el código del popup a las exclusiones de minificación para evitar una doble ejecución.

¿Vale la pena molestarse con código personalizado en 2026?

Ambos plugins, Contact Form 7 y Elementor Pro, se desarrollan y actualizan activamente. CF7 mantiene su posición como el plugin de formularios más popular de WordPress con más de 5 millones de instalaciones activas. Elementor Pro se usa en uno de cada cuatro sitios WordPress.

Sin embargo, el error descrito aquí no se ha corregido a nivel del núcleo de ninguno de los dos plugins y probablemente nunca se hará. La razón es arquitectónica: CF7 es responsable de los formularios, Elementor del contenido dinámico, e inicializar scripts en DOM cargado dinámicamente sigue siendo responsabilidad del desarrollador.

La buena noticia: la corrección es trivial, el código se añade una vez y no requiere mantenimiento. Elija el primer método (el evento elementor/popup/show): es el más limpio y puede olvidarse del problema. Si el formulario en el popup se usa para escenarios críticos de generación de leads, añada también el restablecimiento de campos y el cierre automático: la experiencia del usuario mejorará notablemente.

Vea el video anterior: muestra el proceso completo de configuración de un popup con Contact Form 7 en Elementor. Esta guía visual paso a paso complementa los fragmentos proporcionados aquí y le ayuda a evitar errores durante el montaje.