Javascript Survey Creation & Management. Made Easy
surveyJS let's you create a survey from a JSON and manage all the process ( fields validation, local storage and form data are already managed )
You can manage:
surveyJS is compatible with ( desktop & mobile versions ):
* for compatibility with IE 10/11 and old versions of Edge you have to load some JS polyfills ( from polyfill.io or else ):
Array.from
Element.prototype.closest
Element.prototype.matches
Promise
Promise.prototype.finally
AbortController
fetch
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Array.from,Element.prototype.closest,Element.prototype.matches,Promise,Promise.prototype.finally,AbortController,fetch"></script>
surveyJS is developed with these dependencies:
Include css and js in your page.
<link href="https://unpkg.com/surveyjs/dist/survey.min.css" rel="stylesheet" type="text/css"> <script src="https://unpkg.com/formjs-plugin@3/dist/formjs.min.js"></script> <script src="https://unpkg.com/surveyjs/dist/surveyjs.min.js"></script>
or from jsDelivr:
<link href="https://cdn.jsdelivr.net/gh/simplysayhi/surveyJS/dist/survey.min.css" rel="stylesheet" type="text/css"> <script src="https://cdn.jsdelivr.net/gh/simplysayhi/formJS@3/dist/formjs.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/simplysayhi/surveyJS/dist/surveyjs.min.js"></script>
or install via NPM:
npm install surveyjs
and use as ES6 module:
import Survey from 'surveyjs';
or use as CommonJS module:
var Survey = require('surveyjs');
This is the basic HTML structure to initialize the survey ( element with data-surveyjs-body
will be filled with questions & answers. ):
<div class="surveyjs-container" data-surveyjs-container> <form action="page.jsp" name="surveyjs-form" class="surveyjs-form" data-surveyjs-form novalidate> <div class="surveyjs-body questionsList clearfix" data-surveyjs-body></div> <div class="surveyjs-footer"> <button type="submit">SEND</button> </div> </form> </div>
Mandatory attributes:
data-surveyjs-*
attributes
action
the page where to send the users answers ( with the AJAX call )
novalidate
prevent the HTML5 default validation of required fields ( they will be validated via JS )
CSS classes ( like .surveyjs-*
) are optional but used to give styles to the demos.
For custom graphic inputs, using CSS class surveyjs-custom-inputs
on the Survey container ( with class "surveyjs-container" ) is suggested in order to default Survey CSS be applied.
You must specify the url to retrieve the JSON data to build the survey.
var formEl = document.querySelector('[data-surveyjs-form]'); var options = { url: 'json/survey.json' }; var mySurvey = new Survey( formEl, options ); mySurvey.init().then(function( response ){ console.log('SURVEY DATA RETRIEVED', response); });
var formEl = '.my-container .my-form';
url
surveyJS has some more properties:
data
: the survey data structure returned from the AJAX call.
internals
: objects & methods used internally ( formInstance
can be useful also for you ).
isInitialized
: whether or not the init method was called on the Survey instance.
version
: the version number.
Example of JSON file for the survey is here.
Note that the AJAX call to get the JSON file MUST return it as JSON string or JSON object.
The survey will be built if status
is "success" and questions
is a non-empty array.
{ "status": "success", // MANDATORY, IF "success" THE SURVEY WILL BE BUILT "data": { "id": 0, "title": "My Survey", "description": "Answer the questions", // LIST OF ALL QUESTIONS "questions": [ // RADIO ANSWER { "id": "1", "question": "Do you have pets?", "answers": [ // EACH ANSWER IS AN OBJECT WITH SOME DATA { "id": "1", "type": "radio", "answer": "Yes" }, { "id": "2", "type": "radio", "answer": "No" } ], // IF required IF FOUND, THE ANSWER FIELDS WILL HAVE THE required ATTRIBUTE (SO THIS QUESTION REQUIRE AN ANSWER) "required": "", // THE POSITION TO SET FOR THE QUESTION (USED ALSO FOR ANSWERS - SEE ABOVE) - THIS IS OPTIONAL "sort": 1 }, // VALIDATE INPUT FIELD ONLY IF FILLED ( validateIfFilled ) AND WITH SPECIFIC subtype VALIDATION ( SEE FORM JS PLUGIN ) { "id": "1b0", "question": "What's your name?", "answers": [ { "id": "1ba0", "type": "text", "answer": "", "subtype": "alphabeticExtended" } ], "validateIfFilled": "", "sort": 0 }, // TEXT INPUT ANSWER NOT MANDATORY { "id": "1b", "question": "Do you have pets?", "answers": [ { "id": "1ba", "type": "text", "answer": "" } ], "sort": 2 }, // INPUT WITH SUBTYPE ( IF TYPE EXISTS IN HTML YOU CAN SET IT AS "type" -> "type": "email" ) { "id": "1c", "question": "What's your email address?", "answers": [ { "id": "1c", "type": "text", "answer": "", "subtype": "email" } ], "required": "", "sort": 1 }, { "id": "2", "question": "Which are your hobbies/interests?", "answers": [ { "id": "6", "type": "radio", "answer": "Music", "sort": 5 }, { "id": "7", "type": "radio", "answer": "Photography", "sort": 2 }, { "id": "8", "type": "radio", "answer": "Technology", "sort": 3 }, { "id": "9", "type": "radio", "answer": "Games", "sort": 4 }, { "id": "10", "type": "radio", "answer": "Reading", "sort": 1 }, { "id": "11", "type": "radio", "answer": "Wellness", "sort": 6 }, { "id": "100", "type": "radio", "answer": "Other...", "sort": 7 } ], "required": "", "sort": 3 }, // TEXTARE { "id": "3", "question": "If yes, specify which sport:", "answers": [ { "id": "18", "type": "textarea", "answer": "", "maxlength": 300 } ], "sort": 4 }, // RELATED FIELD ANSWER WITH INPUT TEXT ( SEE attribute FIELD ) { "id": "5", "question": "What is important for you about the loyalty program?", "answers": [ { "id": "19", "type": "radio", "answer": "It should be funny" }, { "id": "20", "type": "radio", "answer": "It does not require much time" }, { "id": "200", "type": "radio", "answer": "Other...", "attribute": { "id": "", "type": "text", "answers": "" } } ], "required": "", "sort": 6 }, // NESTED ANSWERS ( SEE nested ATTRIBUTE ) // RELATED FIELD ANSWER WITH SELECT FIELD ( SEE attribute FIELD ) { "id": "6", "question": "How do you prefer to be interact with the loyalty program?", "answers": [ { "id": "21", "type": "radio", "answer": "Website", "nested": [ { "id": "21a", "type": "radio", "answer": "Pc", "sort": 1 }, { "id": "21b", "type": "radio", "answer": "Smartphone", "sort": 2 }, { "id": "21c", "type": "radio", "answer": "Tablet", "sort": 3 } ], "sort": 1 }, { "id": "21aPlus", "type": "radio", "answer": "Website", "attribute": [ { "id": "21aa", "type": "option", "answer": "Pc", "sort": 1 }, { "id": "21ba", "type": "option", "answer": "Smartphone", "sort": 2 }, { "id": "21ca", "type": "option", "answer": "Tablet", "sort": 3 } ], "sort": 6 }, { "id": "24", "type": "radio", "answer": "App", "sort": 3 }, { "id": "25", "type": "radio", "answer": "Phone", "sort": 4 }, { "id": "26", "type": "radio", "answer": "With loyalty card", "sort": 5 } ], "required": "", "sort": 7 }, // CHECKBOX ANSWER WITH MULTIPLE CHOICE ( maxChoice: NUMBER OF MAXIMUM SELECTABLE ANSWERS ) { "id": "8", "question": "What do you like the most about this edition?", "answers": [ { "id": "43", "type": "checkbox", "answer": "Clear of communication", "sort": 1 }, { "id": "44", "type": "checkbox", "answer": "Simplicity of usage", "sort": 2 }, { "id": "45", "type": "checkbox", "answer": "Graphic style and language", "sort": 3 }, { "id": "46", "type": "checkbox", "answer": "Duration", "sort": 4 }, { "id": "47", "type": "checkbox", "answer": "Content/prizes", "sort": 5 } ], "checks": "[1,2]", "required": "", "sort": 9 }, // SELECT ANSWER { "id": "9", "question": "How much is important that the loyalty program is funny?", "answers": [ { "id": "160", "type": "option", "answer": "Not much", "sort": 1 }, { "id": "161", "type": "option", "answer": "Medium", "sort": 2 }, { "id": "162", "type": "option", "answer": "Much", "sort": 3 } ], "required": "", "sort": 10 }, // PRIVACY CHECK { "id": "22", "question": "hidden-privacy", "answers": [ { "id": "44", "type": "radio", "answer": "Yes", "sort": 1 }, { "id": "45", "type": "radio", "answer": "No", "sort": 2 } ], "required": "", "sort": 12 } ] } }
Note:
To bind the privacy acceptance field to the survey you can set a question object as shown in the JSON above ( see PRIVACY CHECK ):
<div class="surveyjs-question-box" data-formjs-question> <div class="surveyjs-answers-box"> <div class="form-check-inline"> <input data-exclude-storage data-name="bind-surveyjs-answer"/> <label> <span></span> </label> </div> <div class="form-check-inline"> <input data-exclude-storage data-name="bind-surveyjs-answer"/> <label> <span></span> </label> </div> </div> </div>
You can initialize the survey with some extra options:
cssClasses
Type: object
JS object with keys:
fieldErrorFeedback
Type: boolean
whether or not to load the field error message inside the HTML ( shown on failed field validation )
fieldErrorMessage *
Type: string
Text to show on generic field error validation.
See below for default messages ( depending on the language ).
fieldErrorMessageMultiChoice *
Type: string
Text to show on error validation of checkboxes with multiple choice.
See below for default messages ( depending on the language ).
fieldOptions
Type: object
JS object with field options passed to isValidField method of Form instance.
See formJS plugin for details.
formOptions
Type: object
JS object with form options passed to the Form instance creation.
See formJS plugin for details.
initAjaxOptions
Type: object
JS object with ajax options passed to the fetch call to retrieve the survey data.
lang
Type: string
language for messages (eg: loading box), select default option etc...
other values: 'it'
loadingBox *
Type: string
HTML container and text for the loading box (used when requesting the survey JSON file).
See below for default messages ( depending on the language ).
maxChoiceText *
Type: string
This text can be used for quesions that let the user choose more than one answer (checkboxes).
See below for default messages ( depending on the language ).
selectFirstOption *
Type: string
Used if an option with empty id is NOT found in the JSON.
See below for default messages ( depending on the language ).
templates.fieldError
Type: string
HTML string to be used as field error container ( when showing an error message on failed field validation ).
See Templates section.
templates.input
Type: string
HTML string to be used for input fields when generating the survey
See Templates section.
templates.inputGroup
Type: string
HTML string to be used for input group answers when generating the survey.
See Bootstrap Input Group
See Templates section.
templates.inputTag
Type: string
HTML string to be used as input tags when generating the survey
See Templates section.
templates.labelTag
Type: string
HTML string to be used for label tags when generating the survey
See Templates section.
templates.question
Type: string
HTML string to be used as question (and answers) container when generating the survey
See Templates section.
templates.select
Type: string
HTML string to be used for select answers when generating the survey
See Templates section.
templates.selectTag
Type: string
HTML string to be used for select tags when generating the survey
See Templates section.
templates.textarea
Type: string
HTML string to be used for textarea answers when generating the survey
See Templates section.
textareaPlaceholder *
Type: string
See below for default messages ( depending on the language ).
useLocalStorage
Type: boolean
Whether or not to use JS local storage to save user answers while compiling the survey.
When reloading the page, JS local storage will be used to polulate the questions with the answers selected/typed by the user.
Note that localStorage for the survey will be cleared on submit success.
destroy()
Description:
Remove all events listeners from the Survey and Form instances.
init()
Description:
This will retrieve the survey data and initialize the Form instance for it.
Return value:
Promise
You can use these methods ONLY from the original Survey object ( not from a new instance - but you can always set each single property/method from the class prototype ):
addLanguage( langString, langObject )
Parameters:
You MUST pass all the key/value pairs to set a new language.
You can pass only new key/value pairs if you want to override an existing language.
Description:
This will affect ONLY new instances
This can be used to add a new language or override an existing one.
setOptions( options )
Parameters:
Description:
This will affect ONLY new instances
You can initialize the survey with your custom HTML templates for:
Remember to use ALL the data-* attributes and placeholders with the curly braces, for example {{questionId}}, in your custom code.
Here some examples of initialization with custom templates ( these are, actually, the basic templates ):
Custom Template for Loading Box
{ url: 'json/survey.json', loadingBox: '<div class="surveyjs-loading" data-surveyjs-loading><i class="glyphicon glyphicon-refresh icon-spin"></i> Loading...</div>' }
Custom Template for Fields Error
{ url: 'json/survey.json', templates: { fieldError: '<div class="surveyjs-field-error-message">{{fieldErrorMessage}}</div>' } }
Custom Template for Inputs
{ url: 'json/survey.json', templates: { input: '<div class="surveyjs-single-answer surveyjs-input-container surveyjs-answer-{{answerType}} form-check" data-answer-index="{{answerIndex}}">'+ '{{inputTagCode}}'+ '{{labelTagCode}}'+ '</div>' } }
Custom Template for Input Groups
{ url: 'json/survey.json', templates: { inputGroup: '<div class="surveyjs-single-answer input-group" data-answer-index="{{answerIndex}}">'+ '<div class="input-group-prepend">'+ '<div class="input-group-text form-check surveyjs-answer-{{answerType}}">'+ '<input type="{{answerType}}" name="surveyjs-answer-{{questionNumber}}" id="{{answerCode}}" data-answer-id="{{answerId}}" value="{{answerIdValue}}" {{attrRequired}} data-require-more="" class="surveyjs-input surveyjs-radio form-check-input" />'+ '<label for="{{answerCode}}" class="surveyjs-label form-check-label">{{answerString}}</label>'+ '</div>'+ '</div>'+ '{{relatedAnswerField}}'+ '</div>' } }
Custom Template for Input Tags
{ url: 'json/survey.json', templates: { inputTag: '<input type="{{answerType}}" {{attrSubtype}} name="surveyjs-answer-{{questionNumber}}{{addMoreName}}" class="surveyjs-input surveyjs-{{answerType}} {{fieldClass}}" id="{{answerCode}}" {{nestedAnswer}} data-answer-root="{{progIdsJoined}}" data-answer-id="{{answerId}}" value="{{answerIdValue}}" {{attrRequired}} {{attrChecks}} {{attrRequiredFrom}} />' } }
Custom Template for Label Tags
{ url: 'json/survey.json', templates: { labelTag: '<label for="{{answerCode}}" class="surveyjs-label {{labelClass}}">{{answerString}}</label>' } }
Custom Template for Questions
{ url: 'json/survey.json', templates: { question: '<div data-question-id="{{questionId}}" data-question-index="{{questionNumber}}" data-formjs-question class="surveyjs-question-box clearfix">'+ '<div class="surveyjs-question-header">Question {{questionNumber}}</div>'+ '<div class="surveyjs-question-body">'+ '<div class="surveyjs-question-text">{{questionText}}</div>'+ '<div class="surveyjs-answers-box form-group clearfix">'+ '{{answersHtml}}'+ '{{fieldErrorTemplate}}'+ '</div>'+ '</div>'+ '</div>' } }
Custom Template for Selects
{ url: 'json/survey.json', templates: { select: '<div class="surveyjs-single-answer surveyjs-answer-select" data-answer-index="{{answerIndex}}">'+ '{{selectTagCode}}'+ '</div>' } }
Custom Template for Select Tags
{ url: 'json/survey.json', templates: { selectTag: '<select id="{{answerCode}}" name="surveyjs-answer-{{questionNumber}}{{addMoreName}}" class="surveyjs-select {{fieldClass}}" {{attrRequired}} {{nestedAnswer}} data-answer-root="{{progIdsJoined}}" {{attrRequiredFrom}}>'+ '{{optionsHtml}}'+ '</select>' } }
Custom Template for Textareas
{ url: 'json/survey.json', templates: { textarea: '<div class="surveyjs-single-answer surveyjs-answer-textarea">'+ '<textarea id="{{answerCode}}" data-answer-id="{{answerId}}" {{nestedAnswer}} name="surveyjs-answer-{{questionNumber}}" {{attrRequired}} class="surveyjs-textarea {{fieldClass}}" {{answerMaxlength}} rows="6" placeholder="{{answerPlaceholder}}"></textarea>'+ '</div>' } }
If you find a bug, please report it here