GenZformHelp Center
FeaturesLogic & Calculations

Custom JavaScript

Add custom logic to GenZform forms using JavaScript hooks for calculations, conditional navigation, real-time validation, and form submission handling.

GenZform forms support custom JavaScript through the on_next_page_scripts feature. This allows you to add calculations, conditional navigation, real-time field validation, and custom submission handling directly in your form.

Custom JavaScript is an advanced feature for forms that need dynamic calculations or complex logic. Most forms don't need this—use the built-in field types and conditional visibility for standard use cases.

Available Hooks

GenZform provides three JavaScript hooks you can define in your form's on_next_page_scripts:

HookWhen It's CalledReturn Value
window.on_next_button_clickUser clicks "Next" button{ can_proceed, custom_variables, page_no }
window.on_form_submitUser submits the formFinal data object to submit
window.on_input_blurUser leaves any input field{ custom_variables, formData }

on_next_button_click

Called when the user clicks the "Next" button to move between pages. Use this for:

  • Page-specific calculations
  • Custom validation
  • Conditional page navigation
  • Storing calculated values
window.on_next_button_click = function(formData, custom_variables, current_page_index) {
  // Example: Calculate total on page 1
  if (current_page_index === 0) {
    const quantity = parseFloat(formData.quantity) || 0;
    const price = parseFloat(formData.unit_price) || 0;
    const subtotal = quantity * price;
    const tax = subtotal * 0.1;

    // Store in custom_variables for use in later pages
    custom_variables.subtotal = subtotal.toFixed(2);
    custom_variables.tax = tax.toFixed(2);
    custom_variables.total = (subtotal + tax).toFixed(2);
  }

  return {
    can_proceed: true,           // Allow/block navigation
    custom_variables: custom_variables,  // Updated variables
    page_no: current_page_index + 1      // Which page to go to
  };
};

Parameters

ParameterTypeDescription
formDataObjectCurrent form field values keyed by field ID
custom_variablesObjectPersistent variables you can store between pages
current_page_indexNumberCurrent page index (0-based)

Return Value

PropertyTypeDescription
can_proceedBooleanIf false, navigation is blocked
custom_variablesObjectUpdated custom variables to persist
page_noNumberPage index to navigate to

Conditional Navigation

Skip pages based on user input:

window.on_next_button_click = function(formData, custom_variables, current_page_index) {
  // Skip page 2 (index 1) if user selected "No" for detailed info
  if (current_page_index === 0 && formData.want_details === 'no') {
    return {
      can_proceed: true,
      custom_variables: custom_variables,
      page_no: 2  // Skip to page 3 (index 2)
    };
  }

  return {
    can_proceed: true,
    custom_variables: custom_variables,
    page_no: current_page_index + 1
  };
};

Validation with Error Messages

Block navigation and show an error:

window.on_next_button_click = function(formData, custom_variables, current_page_index) {
  if (current_page_index === 0) {
    const email = formData.email || '';

    // Validate email domain
    if (!email.endsWith('@company.com')) {
      alert('Please use your company email address');
      return {
        can_proceed: false,
        custom_variables: custom_variables,
        page_no: current_page_index
      };
    }
  }

  return {
    can_proceed: true,
    custom_variables: custom_variables,
    page_no: current_page_index + 1
  };
};

on_form_submit

Called when the user submits the final page. Use this for:

  • Final calculations before submission
  • Data transformation
  • Adding computed fields to the submission
window.on_form_submit = function(formData, custom_variables, current_page_index) {
  // Add calculated totals to submission data
  return {
    ...formData,
    calculated_total: custom_variables.total,
    calculated_tax: custom_variables.tax,
    submission_timestamp: new Date().toISOString()
  };
};

Parameters

ParameterTypeDescription
formDataObjectAll form field values
custom_variablesObjectCustom variables from previous pages
current_page_indexNumberFinal page index

Return Value

Return the final data object to be submitted. This object will be saved as the form response.

on_input_blur

Called when any input field loses focus (user clicks/tabs away). Use this for:

  • Real-time calculations as the user types
  • Live total updates
  • Field-level validation feedback
  • Dependent field updates
window.on_input_blur = function(formData, custom_variables, current_page_index, field_id) {
  // Update running total whenever quantity or price changes
  if (field_id === 'quantity' || field_id === 'unit_price') {
    const quantity = parseFloat(formData.quantity) || 0;
    const price = parseFloat(formData.unit_price) || 0;
    custom_variables.running_total = (quantity * price).toFixed(2);
  }

  return {
    custom_variables: custom_variables,
    formData: formData
  };
};

Parameters

ParameterTypeDescription
formDataObjectCurrent form field values
custom_variablesObjectCustom variables
current_page_indexNumberCurrent page index
field_idStringID of the field that lost focus

Return Value

PropertyTypeDescription
custom_variablesObjectUpdated custom variables
formDataObjectUpdated form data (for field modifications)

Using Custom Variables

Custom variables are persistent across pages and can be displayed in your form using macros.

Storing Values

// In on_next_button_click or on_input_blur
custom_variables.monthly_payment = payment.toFixed(2);
custom_variables.loan_term_years = 30;
custom_variables.interest_rate = '6.5%';

Displaying Values

Use the {{variable_name}} macro syntax in any text field, label, or content box:

Your estimated monthly payment is ${{monthly_payment}}

Dynamic Content

Use JavaScript expressions in macros:

{{total > 1000 ? 'Large order discount applied!' : 'Add more items for a discount'}}

Best Practices

Always Validate Numeric Inputs

// Good: Handles missing or invalid values
const value = parseFloat(formData.amount) || 0;

// Bad: Will fail if amount is empty or non-numeric
const value = formData.amount;

Handle Edge Cases

// Check for division by zero
if (denominator !== 0) {
  result = numerator / denominator;
} else {
  result = 0;
}

// Check for negative values when they don't make sense
const quantity = Math.max(0, parseFloat(formData.quantity) || 0);

Format Currency Values

// Use toFixed for consistent decimal places
custom_variables.total = total.toFixed(2);  // "123.45"

// For display with currency symbol, use in the form label:
// "Total: ${{total}}"

Use Descriptive Variable Names

// Good: Clear what each variable represents
custom_variables.monthly_loan_payment = payment;
custom_variables.total_interest_paid = totalInterest;

// Bad: Unclear abbreviations
custom_variables.mlp = payment;
custom_variables.tip = totalInterest;

Example: Loan Calculator

A complete example showing all three hooks working together:

// Real-time updates as user fills in values
window.on_input_blur = function(formData, custom_variables, current_page_index, field_id) {
  if (current_page_index === 0) {
    const principal = parseFloat(formData.loan_amount) || 0;
    const rate = parseFloat(formData.interest_rate) || 0;
    const years = parseFloat(formData.loan_term) || 1;

    if (principal > 0 && rate > 0) {
      const monthlyRate = rate / 100 / 12;
      const months = years * 12;
      const payment = principal * (monthlyRate * Math.pow(1 + monthlyRate, months))
                      / (Math.pow(1 + monthlyRate, months) - 1);

      custom_variables.estimated_payment = payment.toFixed(2);
    }
  }

  return { custom_variables, formData };
};

// Finalize calculation when moving to results page
window.on_next_button_click = function(formData, custom_variables, current_page_index) {
  if (current_page_index === 0) {
    const principal = parseFloat(formData.loan_amount) || 0;
    const rate = parseFloat(formData.interest_rate) || 0;
    const years = parseFloat(formData.loan_term) || 1;

    // Validation
    if (principal <= 0) {
      alert('Please enter a valid loan amount');
      return { can_proceed: false, custom_variables, page_no: 0 };
    }

    const monthlyRate = rate / 100 / 12;
    const months = years * 12;
    const payment = principal * (monthlyRate * Math.pow(1 + monthlyRate, months))
                    / (Math.pow(1 + monthlyRate, months) - 1);
    const totalPaid = payment * months;
    const totalInterest = totalPaid - principal;

    custom_variables.monthly_payment = payment.toFixed(2);
    custom_variables.total_paid = totalPaid.toFixed(2);
    custom_variables.total_interest = totalInterest.toFixed(2);
  }

  return {
    can_proceed: true,
    custom_variables,
    page_no: current_page_index + 1
  };
};

// Add summary to submission
window.on_form_submit = function(formData, custom_variables, current_page_index) {
  return {
    ...formData,
    calculated_monthly_payment: custom_variables.monthly_payment,
    calculated_total_interest: custom_variables.total_interest
  };
};

Reserved Variables

These variables are reserved by GenZform and should not be overwritten:

VariableDescription
{{score}}Quiz score (total points earned)
{{percentage}}Quiz percentage (0-100)
{{max_score}}Maximum possible quiz score

Frequently Asked Questions

Where do I add custom JavaScript?

Custom JavaScript goes in the on_next_page_scripts field of your form's JSON configuration. If you're using the AI chat to build forms, describe the calculation logic you need and the AI will generate the appropriate scripts.

Can I use external libraries?

No. The scripts run in a sandboxed environment without access to external libraries. Use vanilla JavaScript for all calculations.

How do I debug my scripts?

Use console.log() statements to debug. Open your browser's developer tools (F12) and check the Console tab while testing your form.

Why isn't my calculation working?

Common issues: (1) Not parsing string values to numbers with parseFloat(), (2) Missing fallback values (|| 0), (3) Incorrect page index in conditional logic, (4) Typos in field IDs.


On this page