blog

Web Form Navigation System: The Ultimate Guide (Part 3)

In the first part of this guide, we will talk about various types of form navigation you can implement with SurveyJS Form Library, including progress indicators, tabs, Previous/Next buttons, jump links, and side navigation (TOC). All these navigation features come out of the box and only require selecting a few configuration settings.

The second part of the guide covers the process of adding a new navigation item with a custom function (not just a default button with a modified caption, but a button that calls any function you assign to it).

In the third part of the ultimate form navigation guide, we will show how to create and configure navigation controls outside the form area, which will listen to changes in the form and update the current form page number in real time. We will also show how to implement external navigation buttons, which can be placed anywhere on a page and will allow you to move forward or backward through a form or survey without clicking any buttons inside the form itself. With this external navigation in place, you can have a pure form widget with no navigation elements and control it from outside the form area.

SurveyJS: Online Form Navigation

Prerequisites

SurveyJS supports integration with all popular JavaScript frameworks. In this tutorial, we will be adding a survey to a React application (please refer to the getting started guide for more details). First, install the survey-react-ui npm package using the following command:

npm install survey-react-ui --save

The survey-core and react packages will be installed automatically because both are listed in survey-react-ui dependencies. Once they're installed, import the Model module from the survey-core package and the Survey module from the rendering survey-react-ui package, and configure the style of a form by importing either of the two style sheets:

// Default V2 theme
import 'survey-core/defaultV2.min.css';

// Modern theme
// import 'survey-core/modern.min.css';

As you probably know, SurveyJS uses the JSON format to describe survey data models and exchange those JSON objects between the client and the server. So, you can either create a data model in JSON manually (please refer to the API references to learn more about its properties, methods, and events) or use a free full-scale demo of Survey Creator, a self-hosted no-code form builder tool. Once done, import survey data in JSON format as well - in our example, it’s imported from the json.js file. Here is what you should have in your EDI:

import React from "react";
import { Model } from "survey-core";
import { Survey } from "survey-react-ui";
import "survey-core/defaultV2.min.css";
import "./index.css";
import { json } from "./json";

Adding Event Listeners

Declare a new survey state variable and initialize it to a new instance of the Model class from the survey-core library using the json object as a parameter. The useState function is a React hook that lets you add the React state to function components.

function SurveyComponent() {
  const [survey] = React.useState(new Model(json));
// ...
}

Now add an event listener to the onCurrentPageChanged event of the survey object, which is raised when a user navigates to a different page in the survey. The listener function increments the pageChangeCounter state variable by 1 using the setPageChangeCounter function, which triggers re-rendering of the component.

survey.onCurrentPageChanged.add((sender, options) => {
  setPageChangeCounter(pageChangeCounter + 1);
});

Add another event listener to the onComplete event of the survey object, which is raised when the survey is completed. Like the previous listener, it also increments the pageChangeCounter state variable by 1.

Register one more callback function, which will be executed when a respondent completes the survey. But this time, call the log method on the global console object using JSON.stringify() as a parameter to log survey data to the console in JSON format.

survey.onComplete.add((sender, options) => {
  setPageChangeCounter(pageChangeCounter + 1);
});
survey.onComplete.add((sender, options) => {
  console.log(JSON.stringify(sender.data, null, 3));
});

For more information on how to access and store user responses, refer to the following guide: Handle Survey Results.

Now declare a second pageChangeCounter state variable and initialize it to 0 using the useState hook. This variable will be used to keep track of the number of times a user has navigated between survey pages.

const [pageChangeCounter, setPageChangeCounter] = React.useState(0);

Configuring Survey Navigation

Declare three functions to control navigation of a survey. The prevPageFunc() function calls the prevPage() method of the survey object and moves the survey to the previous page. The nextPageFunc() function calls the nextPage() method and moves the survey to the next page. And the completePageFunc() function calls the completeLastPage() method to complete the survey.

const prevPageFunc = () => { survey.prevPage(); };
const nextPageFunc = () => { survey.nextPage(); };
const completePageFunc = () => { survey.completeLastPage(); };

To change an active survey page based on the selected page number, specify the currentPageNo property as shown below:

const changePageIndexDirectly = (evt) => { survey.currentPageNo = evt.target.value; };

Now create a constant variable renderButton and initialize it to an arrow function. The function takes text (the label of the button), func (the function to be called when the button is clicked), and canRender (a boolean indicating whether the button should be rendered or not) as parameters. If the canRender parameter is false, the button is not rendered.

const renderButton = (text, func, canRender) => {
  if (!canRender) return undefined;
  return (<button className="navigation-button" onClick={func}>{text}</button>);
};

Adding an External Navigation Component

In order to create a navigation component outside the form, define a renderExternalNavigation function using the arrow function with no parameters. The function first checks if the survey is currently in a "running" state, meaning that it is active and ready to be navigated. If the survey is not running, no navigation controls will be rendered.

If the survey is running, the function creates and returns a block of HTML elements for survey navigation controls. The first child div contains the Previous, Next, and Complete buttons, the current page number, and the total number of pages in the survey. The second child div contains text that instructs the user on how to directly navigate to a specific page in the survey without going through the validation process (for example, if certain questions of a survey are set as required), and a list of page options to select from.

const renderExternalNavigation = () => {
       if(survey.state !== "running") return undefined;
       const progressText = "Page " + (survey.currentPageNo + 1) + " of " + survey.visiblePages.length;
       const progressSpan = <span className="navigation-text">{progressText}</span>;
       const pageSelectorOptions = [];
       for (let i = 0; i < survey.visiblePages.length; i++) {
           const name = "Page " + (i + 1);
           pageSelectorOptions.push(<option key={i} value={i}>{name}</option>);
       }
       const pageSelector = (
           <select className="navigation-page-selector" value={survey.currentPageNo} onChange={changePageIndexDirectly}>
               {pageSelectorOptions}
           </select>
       );
       return <div className="navigation-block">
           <div>
               {progressSpan}
               {renderButton("Prev", prevPageFunc, !survey.isFirstPage)}
               {renderButton("Next", nextPageFunc, !survey.isLastPage)}
               {renderButton("Complete", completePageFunc, survey.isLastPage)}
           </div>
           <div>
               <span className="navigation-text">Go to page directly without validation:</span>
               {pageSelector}
           </div>
        </div>
   };

   return (
       <div>
           {renderExternalNavigation()}
           <Survey model={survey} />
       </div>
   );
// ...
}

As you created the custom external navigation, you may wish to hide the default survey navigation bar by disabling the survey.showNavigationButtons option.

This is how the navigation bar appears for a running survey:

SurveyJS: External Nagivation

Demo

If you want to see a full-scale Code Sandbox demo, click the 'Edit in Code Sandbox' button below:

Edit friendly-meitner-dco9ff

Summary

As you can see, SurveyJS provides you with multiple navigation options out of the box and lets you create custom components that are controlled from outside the form widget. In the final part of this guide, we will show you how to create a custom navigation component - a percentage progress bar - within the survey.

Your cookie settings

We use cookies on our site to make your browsing experience more convenient and personal. In some cases, they are essential to making the site work properly. By clicking "Accept All", you consent to the use of all cookies in accordance with our Terms of Use & Privacy Statement. However, you may visit "Cookie settings" to provide a controlled consent.

Your renewal subscription expires soon.

Since the license is perpetual, you will still have permanent access to the product versions released within the first 12 month of the original purchase date.

If you wish to continue receiving technical support from our Help Desk specialists and maintain access to the latest product updates, make sure to renew your subscription by clicking the "Renew" button below.

Your renewal subscription has expired.

Since the license is perpetual, you will still have permanent access to the product versions released within the first 12 month of the original purchase date.

If you wish to continue receiving technical support from our Help Desk specialists and maintain access to the latest product updates, make sure to renew your subscription by clicking the "Renew" button below.