UNPKG

form-functionality-library

Version:

A modular, flexible form functionality library for Webflow forms supporting single-step, multi-step, and branching forms

391 lines 15 kB
/** * DIAGNOSTIC Multi-step navigation - ULTRA-VERBOSE LOGGING * This version logs EVERYTHING to help diagnose the persistent visibility issues */ import { SELECTORS } from '../config.js'; import { queryAllByAttr, getAttrValue, delegateEvent } from './utils.js'; let initialized = false; let steps = []; let currentStepIndex = 0; let debugMode = true; function log(message, data) { const timestamp = new Date().toISOString(); console.log(`🔍 [DIAGNOSTIC ${timestamp}] ${message}`, data || ''); } function logError(message, data) { const timestamp = new Date().toISOString(); console.error(`❌ [DIAGNOSTIC ${timestamp}] ${message}`, data || ''); } function logDOM(element, action) { const computedStyle = getComputedStyle(element); log(`DOM ${action}:`, { element: element.tagName, id: element.id, className: element.className, inlineStyles: { display: element.style.display, visibility: element.style.visibility, opacity: element.style.opacity }, computedStyles: { display: computedStyle.display, visibility: computedStyle.visibility, opacity: computedStyle.opacity, position: computedStyle.position, zIndex: computedStyle.zIndex }, dimensions: { offsetWidth: element.offsetWidth, offsetHeight: element.offsetHeight, clientWidth: element.clientWidth, clientHeight: element.clientHeight }, parentInfo: { tagName: element.parentElement?.tagName, id: element.parentElement?.id, className: element.parentElement?.className } }); } /** * DIAGNOSTIC: Initialize with maximum logging */ export function initMultiStepDiagnostic(root = document) { log('=== DIAGNOSTIC INITIALIZATION START ==='); log('Root element:', { isDocument: root === document, elementType: root.constructor.name, hasQuerySelector: typeof root.querySelector === 'function' }); // Check for existing form libraries log('Checking for existing form libraries:', { window_FormLib: typeof window.FormLib, window_formLibrary: typeof window.formLibrary, existing_scripts: Array.from(document.scripts).map(s => s.src).filter(s => s.includes('form')) }); // Find all steps with detailed analysis log('Searching for steps with selector:', SELECTORS.STEP); const stepElements = queryAllByAttr(SELECTORS.STEP, root); log('Step search results:', { selector: SELECTORS.STEP, foundCount: stepElements.length, allElementsWithDataForm: Array.from(document.querySelectorAll('[data-form]')).map(el => ({ tagName: el.tagName, id: el.id, className: el.className, dataForm: el.getAttribute('data-form') })) }); if (stepElements.length === 0) { logError('No steps found - detailed analysis:', { searchRoot: root === document ? 'document' : 'custom element', allDataFormElements: document.querySelectorAll('[data-form]').length, allStepWrappers: document.querySelectorAll('.step_wrapper').length, allMultiFormSteps: document.querySelectorAll('.multi-form_step').length, possibleStepElements: Array.from(document.querySelectorAll('div')).filter(el => el.className.includes('step') || el.id.includes('step')).map(el => ({ tagName: el.tagName, id: el.id, className: el.className })) }); return; } // Analyze each step in detail steps = Array.from(stepElements).map((element, index) => { log(`Analyzing step ${index}:`); logDOM(element, `STEP_${index}_INITIAL`); const stepWrapper = element.querySelector('.step_wrapper[data-answer]'); const dataAnswer = stepWrapper ? getAttrValue(stepWrapper, 'data-answer') : getAttrValue(element, 'data-answer'); log(`Step ${index} data-answer analysis:`, { hasStepWrapper: !!stepWrapper, stepWrapperDataAnswer: stepWrapper ? getAttrValue(stepWrapper, 'data-answer') : null, directDataAnswer: getAttrValue(element, 'data-answer'), finalDataAnswer: dataAnswer || `step-${index}`, allAttributes: Array.from(element.attributes).map(attr => ({ name: attr.name, value: attr.value })) }); return { element: element, id: dataAnswer || `step-${index}`, index }; }); log('All steps registered:', { totalSteps: steps.length, stepMapping: steps.map((s, i) => ({ index: i, id: s.id, element: { tagName: s.element.tagName, id: s.element.id, className: s.element.className } })) }); // Check for potential conflicts checkForConflicts(); // Initial hiding with detailed logging log('=== INITIAL STEP HIDING ==='); hideAllStepsDiagnostic(); // Show first step log('=== SHOWING FIRST STEP ==='); showStepDiagnostic(0); // Setup radio listeners with detailed logging setupRadioListenersDiagnostic(root); initialized = true; log('=== DIAGNOSTIC INITIALIZATION COMPLETE ===', { initialized, totalSteps: steps.length, currentStepIndex, currentStepId: steps[currentStepIndex]?.id }); } function checkForConflicts() { log('=== CHECKING FOR CONFLICTS ==='); // Check for multiple event listeners const radioButtons = document.querySelectorAll('input[type="radio"][data-go-to]'); log('Radio buttons found:', { count: radioButtons.length, details: Array.from(radioButtons).map((radio, i) => { const htmlRadio = radio; return { index: i, name: htmlRadio.name, value: htmlRadio.value, dataGoTo: radio.getAttribute('data-go-to'), hasListeners: htmlRadio.onclick !== null || htmlRadio.onchange !== null }; }) }); // Check for CSS conflicts const hiddenSteps = document.querySelectorAll('[data-form="step"]'); hiddenSteps.forEach((step, i) => { const computed = getComputedStyle(step); if (computed.display === 'none' || computed.visibility === 'hidden') { log(`Step ${i} already hidden by CSS:`, { element: step, computedDisplay: computed.display, computedVisibility: computed.visibility, possibleCSSRules: 'Check for conflicting CSS' }); } }); // Check for other form libraries log('Window object analysis:', { FormLib: typeof window.FormLib, formLibrary: typeof window.formLibrary, jQuery: typeof window.$, webflow: typeof window.Webflow, allFormLibKeys: Object.keys(window).filter((key) => key.toLowerCase().includes('form')) }); } function hideAllStepsDiagnostic() { log('Hiding all steps - BEFORE state:'); steps.forEach((step, i) => { logDOM(step.element, `BEFORE_HIDE_${i}`); }); steps.forEach((step, i) => { log(`Hiding step ${i} (${step.id}):`); // Record before state const beforeState = { display: step.element.style.display, visibility: step.element.style.visibility, computedDisplay: getComputedStyle(step.element).display, isVisible: step.element.offsetHeight > 0 }; // Apply hiding step.element.style.setProperty('display', 'none', 'important'); step.element.style.setProperty('visibility', 'hidden', 'important'); // Record after state const afterState = { display: step.element.style.display, visibility: step.element.style.visibility, computedDisplay: getComputedStyle(step.element).display, isVisible: step.element.offsetHeight > 0 }; log(`Step ${i} hide result:`, { stepId: step.id, before: beforeState, after: afterState, hideSuccessful: afterState.isVisible === false }); logDOM(step.element, `AFTER_HIDE_${i}`); }); } function showStepDiagnostic(stepIndex) { if (stepIndex < 0 || stepIndex >= steps.length) { logError('Invalid step index:', { stepIndex, totalSteps: steps.length }); return; } const step = steps[stepIndex]; log(`=== SHOWING STEP ${stepIndex} (${step.id}) ===`); // Hide all others first log('Hiding all other steps first...'); hideAllStepsDiagnostic(); // Record before state logDOM(step.element, `BEFORE_SHOW_${stepIndex}`); // Apply showing with maximum force log(`Applying show styles to step ${stepIndex}:`); step.element.style.setProperty('display', 'block', 'important'); step.element.style.setProperty('visibility', 'visible', 'important'); step.element.style.setProperty('opacity', '1', 'important'); // Force layout recalculation step.element.offsetHeight; // Force reflow // Record after state logDOM(step.element, `AFTER_SHOW_${stepIndex}`); // Verify visibility const isNowVisible = step.element.offsetHeight > 0 && step.element.offsetWidth > 0; log(`Step ${stepIndex} show result:`, { stepId: step.id, isVisible: isNowVisible, offsetHeight: step.element.offsetHeight, offsetWidth: step.element.offsetWidth, computedDisplay: getComputedStyle(step.element).display, computedVisibility: getComputedStyle(step.element).visibility }); if (!isNowVisible) { logError(`STEP ${stepIndex} FAILED TO SHOW!`, { stepId: step.id, possibleCauses: [ 'CSS override with higher specificity', 'Parent element hidden', 'JavaScript interference', 'Layout engine issue' ] }); // Check parent elements let parent = step.element.parentElement; let parentLevel = 0; while (parent && parentLevel < 5) { logDOM(parent, `PARENT_${parentLevel}`); parent = parent.parentElement; parentLevel++; } } currentStepIndex = stepIndex; } export function goToStepByIdDiagnostic(stepId) { log(`=== NAVIGATION REQUEST: ${stepId} ===`); const stepIndex = steps.findIndex(step => step.id === stepId); if (stepIndex === -1) { logError('Step not found:', { searchedFor: stepId, availableSteps: steps.map(s => s.id), totalSteps: steps.length, suggestion: 'Check data-answer attributes match exactly' }); return; } log(`Found step ${stepId} at index ${stepIndex}, navigating...`); showStepDiagnostic(stepIndex); } function setupRadioListenersDiagnostic(root) { log('=== SETTING UP RADIO LISTENERS ==='); const radioButtons = document.querySelectorAll('input[type="radio"][data-go-to]'); log('Found radio buttons:', { count: radioButtons.length, buttons: Array.from(radioButtons).map((radio, i) => ({ index: i, name: radio.name, value: radio.value, dataGoTo: radio.getAttribute('data-go-to'), id: radio.id, className: radio.className })) }); const cleanup = delegateEvent(root, 'change', 'input[type="radio"][data-go-to]', (event, target) => { log('=== RADIO BUTTON EVENT TRIGGERED ==='); const radio = target; log('Radio button analysis:', { eventType: event.type, radioName: radio.name, radioValue: radio.value, isChecked: radio.checked, dataGoTo: getAttrValue(radio, 'data-go-to'), eventTarget: event.target, eventCurrentTarget: event.currentTarget, timestamp: new Date().toISOString() }); if (!radio.checked) { log('Radio not checked, ignoring event'); return; } const goToValue = getAttrValue(radio, 'data-go-to'); if (!goToValue) { logError('Radio button missing data-go-to attribute'); return; } log('Processing radio navigation:', { from: steps[currentStepIndex]?.id, to: goToValue, radioGroup: radio.name }); // Apply radio styling applyRadioStylingDiagnostic(radio); // Navigate log('About to navigate...'); goToStepByIdDiagnostic(goToValue); log('=== RADIO BUTTON EVENT COMPLETE ==='); }); log('Radio listeners setup complete'); } function applyRadioStylingDiagnostic(selectedRadio) { log('=== APPLYING RADIO STYLING ==='); const groupName = selectedRadio.name; if (!groupName) return; const activeClass = 'is-active-inputactive'; // Remove from all in group const groupRadios = document.querySelectorAll(`input[type="radio"][name="${groupName}"]`); log('Radio group styling:', { groupName, groupSize: groupRadios.length, activeClass }); groupRadios.forEach((radio, i) => { const r = radio; const label = r.closest('label'); const wasActive = r.classList.contains(activeClass); r.classList.remove(activeClass); label?.classList.remove(activeClass); if (r === selectedRadio) { r.classList.add(activeClass); label?.classList.add(activeClass); log(`Applied active class to radio ${i}`); } else if (wasActive) { log(`Removed active class from radio ${i}`); } }); } export function getDiagnosticState() { return { initialized, currentStepIndex, currentStepId: steps[currentStepIndex]?.id || 'none', totalSteps: steps.length, stepIds: steps.map(s => s.id), allStepsVisible: steps.map(s => ({ id: s.id, isVisible: s.element.offsetHeight > 0, computedDisplay: getComputedStyle(s.element).display, computedVisibility: getComputedStyle(s.element).visibility })) }; } // Expose diagnostic functions globally for manual testing if (typeof window !== 'undefined') { window.FormLibDiagnostic = { showStep: showStepDiagnostic, hideAll: hideAllStepsDiagnostic, goToStep: goToStepByIdDiagnostic, getState: getDiagnosticState, checkConflicts: checkForConflicts, logAllSteps: () => steps.forEach((s, i) => logDOM(s.element, `MANUAL_CHECK_${i}`)) }; } //# sourceMappingURL=multiStep-diagnostic.js.map