surveyJS

Documentation v1.5.2

( all demos: basic demo - demo 1 - demo 2 )

Usage

surveyJS let's you create a survey from a JSON and manage all the process ( fields validation, local storage and JSON construction are already set )
You can manage:

Dependencies

surveyJS is developed with these dependencies:

<!-- BOOTSTRAP CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css">

<!-- AWESOME BOOTSTRAP CHECKBOX CSS ( for custom graphic inputs ) -->
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="stylesheet" href="css/awesome-bootstrap-checkbox.css">

<!-- SURVEY CSS -->
<link rel="stylesheet" href="css/survey.css">

<!-- JQUERY -->
<script src="js/vendors/jquery-1.12.4.min.js"></script>

<!-- SURVEY JS ( with WEB_STORAGE and FORM modules included ) -->
<script src="js/surveyJS-1.5.min.js"></script>

The demo is developed with Bootstrap 3.3.7

HTML

This is the basic HTML structure to initialize the survey ( div with class "survey-body" will be filled with questions & answers. ):

<div class="survey-cont" data-surveyjs-cont>
    <form action="page.jsp" name="survey-form" class="survey-form" data-surveyjs-form novalidate="">
        <div class="survey-body questionsList clearfix" data-surveyjs-body></div>
        <div class="survey-footer">
            <button type="submit">SEND</button>
        </div>
    </form>
</div>

Mandatory attributes:

CSS classes ( like .survey-* ) are optional but used to give styles to the demos.

Basic Initialization

You must specify the url to retrieve the JSON data to build the survey.

SURVEY.setup({ setJSONurl: 'json/survey.json' });

JSON file

Example of JSON file for the survey is here.
( image, title and description are optional )

Below the explanation of the JSON:

{
    "result": "OK", // IF "OK" THE SURVEY WILL BE BUILT
    "survey": {
        "id": 0,
        "image": "img/survey-image.png",
        "title": "My Survey",
        "description": "Answer the questions",
        
        // LIST OF ALL QUESTIONS ( AS OBJECTS )
        "questions": [
        
            // RADIO ANSWER
            {
                // EVERY QUESTION HAS ITS OWN DATA
                "id": "1",
                "question": "Hai animali domestici?",
                "answers": [
                    // EACH ANSWER IS AN OBJECT WITH SOME DATA
                    { "id": "1", "type": "radio", "answer": "Sì", "sort": 1 },
                    { "id": "2", "type": "radio", "answer": "No", "sort": 2 }
                ],
                
                // IF required IS 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
            },
            
            
            
            // TEXT INPUT ANSWER NOT MANDATORY
            {
                "id": "1b",
                "question": "Hai animali domestici?",
                "answers": [
                    { "id": "1ba", "type": "text", "answer": "" }
                ]
            },
            
            
            
            // RELATED FIELD ANSWER WITH INPUT TEXT ( SEE attribute FIELD )
            {
                "id": "5",
                "question": "Quanto è importante che il programma fedeltà...?",
                "answers": [
                    { "id": "19", "type": "radio", "answer": "Sia divertente" },
                    { "id": "20", "type": "radio", "answer": "Non richieda molto impegno di partecipazione" },
                    {
                        "id": "200", "type": "radio", "answer": "Altro...",
                        "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": "Quale MODALITÀ DI PARTECIPAZIONE al programma fedeltà preferisci?",
                "answers": [
                    {
                        "id": "21", "type": "radio", "answer": "Sito",
                        "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": "Sito",
                        "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": "Telefonica", "sort": 4 },
                    { "id": "26", "type": "radio", "answer": "Con utilizzo card/tessera", "sort": 5 }
                ],
                "required": "",
                "sort": 7
            },
            
            
            
            // CHECKBOX ANSWER WITH MULTIPLE CHOICE ( maxChoice: NUMBER OF MAXIMUM SELECTABLE ANSWERS )
            {
                "id": "8",
                "question": "Cosa hai gradito di più del programma in corso?",
                "answers": [
                    { "id": "43", "type": "checkbox", "answer": "Chiarezza della comunicazione", "sort": 1 },
                    { "id": "44", "type": "checkbox", "answer": "Facilità e modalità di partecipazione", "sort": 2 },
                    { "id": "45", "type": "checkbox", "answer": "Stile del programma (grafica e linguaggio)", "sort": 3 },
                    { "id": "46", "type": "checkbox", "answer": "Durata del tempo di partecipazione", "sort": 4 },
                    { "id": "47", "type": "checkbox", "answer": "Contenuti/premi proposti", "sort": 5 }
                ],
                "maxChoice": 2,
                "required": "",
                "sort": 9
            },
            
            
            
            // SELECT ANSWER
            {
                "id": "9",
                "question": "Quanto è importante che il programma fedeltà sia divertente?",
                "answers": [
                            { "id": "160", "type": "option", "answer": "Poco", "sort": 1 },
                            { "id": "161", "type": "option", "answer": "Così così", "sort": 2 },
                            { "id": "162", "type": "option", "answer": "Tanto", "sort": 3 }
                ],
                "required": "",
                "sort": 10
            },
            
            
            
            // PRIVACY CHECK
            {
                "id": "22",
                "question": "hidden-privacy",
                "answers": [
                            { "id": "44", "type": "radio", "answer": "Sì" },
                            { "id": "45", "type": "radio", "answer": "No" }
                ],
                "required": "",
                "sort": 10
            },
        ]
    }
}

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="question-box" data-formjs-question="">
    <div class="answers-box">
        <div class="radio radio-inline">
            <label>
                <input data-exclude-storage="" data-name="bind-survey-answer"/>
                <span></span>
            </label>
        </div>
        <div class="radio radio-inline">
            <label>
                <input data-exclude-storage="" data-name="bind-survey-answer"/>
                <span></span>
            </label>
        </div>
    </div>
</div>

Or with use of graphic inputs:

<div class="question-box" data-formjs-question="">
    <div class="answers-box">
        <div class="radio radio-inline">
            <input data-exclude-storage="" data-name="bind-survey-answer"/>
            <label>
                <span></span>
            </label>
        </div>
        <div class="radio radio-inline">
            <input data-exclude-storage="" data-name="bind-survey-answer"/>
            <label>
                <span></span>
            </label>
        </div>
    </div>
</div>

Custom Initialization

You can initialize the survey with some extra options:

name
type
default
description
lang
string
'en'

language for messages (eg: loading box), select default option etc...
other values: 'it'

onInitComplete( $surveyForm )
function
null

Fires when the AJAX call ( to get the survey JSON ) ends.

Parameters:

  • $surveyForm: jQuery object of the form
onInitError( jqXHR, textStatus, errorThrown, $surveyForm )
function
null

Fires when the AJAX call ( to get the survey JSON ) throws an error.

Parameters:

  • jqXHR: as returned by jQuery
  • textStatus: as returned by jQuery
  • errorThrown: as returned by jQuery
  • $surveyForm: jQuery object of the form
onInitSuccess( json, $surveyForm )
function
null

Fires when all the survey is successfully loaded in the page.

Parameters:

  • json: JSON data returned from AJAX call
  • $surveyForm: jQuery object of the form
onValidation( data )
function
null

Fires on survey form submit or when a field is not valid ans it is mandatory.

the "data" parameter is a JS object like:

{
    event: 'submit',
    fields: [{
        field: $('.myField'),
        result: true
    }]
}
  • event: event name of the event that triggered the validation ( eg: submit, input, change... )
  • fields: array of JS object. each with
    • field: jQuery object of the field
    • result: boolean for question valid or not
beforeSend( answersJSON, $surveyForm )
function
null

Fires before the AJAX call to send the data to the server.

Parameters:

  • answersJSON: JSON object that will be sent
  • $surveyForm: jQuery object of the form

If you want to edit the JSON, you MUST have a return statement with the edited JSON to send it as modified.

onSubmitComplete( $surveyForm )
function
null

Fires when the AJAX call ( to submit the form ) ends.

Parameters:

  • $surveyForm: jQuery object of the form
onSubmitError( jqXHR, textStatus, errorThrown, $surveyForm )
function
null

Fires when the AJAX call to submit the form returns an error.

Parameters:

  • jqXHR: as returned by jQuery
  • textStatus: as returned by jQuery
  • errorThrown: as returned by jQuery
  • $surveyForm: jQuery object of the form
onSubmitSuccess( json, $surveyForm )
function
null

Fires when the AJAX call to submit the form is successfully made

Parameters:

  • json: JSON data returned from AJAX call
  • $surveyForm: jQuery object of the form
fieldOptions
object
{
    validateOnEvents: 'input change'
}

JS object with options to be passed to FORM.isValidField() method.
See FormJS for details.

In particular, "validateOnEvents" is used inside SurveyJS to validate the fields on those events.

fieldErrorFeedback
boolean

true

wether or not to load the field error message inside the HTML ( shown on failed field validation )

fieldErrorTemplate
string

see 'Templates' section below

HTML string to be used as field error container ( when showing an error message on failed field validation )

questionTemplate
string

see 'Templates' section below

HTML string to be used as question (and answers) container when generating the survey

labelTagCode
string
see 'Templates' section below
HTML string to be used for label tags when generating the survey
textareaTemplate
string
see 'Templates' section below
HTML string to be used for textarea answers when generating the survey
selectTagCode
string
see 'Templates' section below
HTML string to be used for select tags when generating the survey
selectTemplate
string
see 'Templates' section below
HTML string to be used for select answers when generating the survey
graphicInput
boolean
false
If true will generate a different code for label-input.
See Awesome Bootstrap Checkbox
inputTagCode
string
see 'Templates' section below
HTML string to be used as input tags when generating the survey
inputTemplate
string
see 'Templates' section below
HTML string to be used for input fields when generating the survey
inputGroupTemplate
string
see 'Templates' section below
HTML string to be used for input group answers when generating the survey.
See Bootstrap Input Group
loadingBox *
string

depends on lang setting (see description)

HTML container and text for the loading box (used when requesting the survey JSON file)

  • en: '<div class="survey-loading"><i class="glyphicon glyphicon-refresh icon-spin"></i> Loading...</div>'
  • it: '<div class="survey-loading"><i class="glyphicon glyphicon-refresh icon-spin"></i> Caricamento in corso...</div>'
selectFirstOption *
string

depends on lang setting (see description)

Used if an option with empty id is NOT found in the JSON

  • en: 'Select your answer...'
  • it: 'Seleziona una risposta...'
textareaPlaceholder *
string

depends on lang setting (see description)

  • en: 'Write here your answer...'
  • it: 'Scrivi la tua risposta...'
maxChoiceText *
string

depends on lang setting (see description)

this text can be used for quesions that let the user choose more than one answer (checkboxes)

  • en: 'ANSWERS MAX'
  • it: 'RISPOSTE MAX'
fieldErrorMessage *
string

Depends on lang setting (see description)

text to show on generic field error validation.

  • en: 'Answer is necessary.'
  • it: 'È necessario rispondere.'
fieldErrorMessageMultiChoice *
string

Depends on lang setting (see description)

text to show on error validation of checkboxes with multiple choice.
{{maxChoiceNum}} is mandatory.

  • en: 'You can choose at maximum {{maxChoiceNum}} answers.'
  • it: 'Puoi scegliere al massimo {{maxChoiceNum}} risposte.'

Here an example of custom initialization:

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    onInitSuccess: function( json, $surveyForm ){
        var $surveyBody = $surveyForm.find('.survey-body'),
            initResult = json.result;

        if( initResult === 'OK' ){
            $('.stackSlider').stackslider({ piles : false });
        } else {
            $surveyForm.find('.survey-footer').remove();
            if ( initResult === 'KO' ){
                $surveyBody.html( '<div class="survey-message">Errore durante il caricamento. Per favore, ricarica la pagina.</div>' );
            } else if( initResult === 'DONE' ) {
                $surveyBody.html( '<div class="survey-message">Hai già completato il questionario.</div>' );
            } else {
                console.log('onInitSuccess -> json.result is ', initResult);
            }
        }
    },
    onInitError: function( jqXHR, textStatus, errorThrown, $surveyForm ){
        $surveyForm.find('.survey-loading').html( '<div class="survey-message">Errore durante il caricamento. Per favore, ricarica la pagina.</div>' );
    },
    onValidation: function( data ){
        // SCROLL THE PAGE TO THE FIRST UNANSWERED QUESTION
        if( data.event === 'submit' ){

            for( var f=0; f<data.fields.length; f++ ){
                var obj = data.fields[f];
                if( !obj.result ){
                    var $box = obj.field.closest('[data-formjs-question]');
                    $('html, body').animate({ scrollTop: $box.offset().top }, 400);
                    break;
                }
            }

        }
    },
    beforeSend: function( JSON, $surveyForm ){
        // USED TO ADD/REMOVE/EDIT ELEMENTS OF THE JSON THAT WILL BE SENT
        return '';
    },
    onSubmitSuccess: function( json, $surveyForm ){
        if( json.result === 'OK' ){
            // REMOVE THE SURVEY FORM FROM THE PAGE AND PRINT A RESPONSE MESSAGE
            $surveyForm.parent().html( '<div class="alert alert-success" role="alert"><p><i class="glyphicon glyphicon-thumbs-up"></i> Great! You completed the mission.</p></div>' );

            // OPEN THE BOOTSTRAP MODAL TO SHOW A CONGRATULATION MESSAGE
            $('#modal-notification').modal('show');
        } else {
            // PRINT THE ERROR MESSAGE AFTER THE FORM
            $surveyForm.closest('.survey-cont').append( '<div class="alert alert-danger" role="alert"><p><i class="glyphicon glyphicon-exclamation-sign"></i> Generic error, please retry.</p></div>' );
        }
    },
    onSubmitError: function( jqXHR, textStatus, errorThrown, $surveyForm ){
        // PRINT THE ERROR MESSAGE AFTER THE FORM
        $surveyForm.closest('.survey-cont').append( '<div class="alert alert-danger" role="alert"><p><i class="glyphicon glyphicon-exclamation-sign"></i> Generic error, please retry.</p></div>' );
    }
});

Localization

You can add a new language ( or override an existing one ), with SURVEY.addLanguage( langString, langMessages ).
This will also set the language used for the survey.

You MUST pass all the pairs key/value as shown below:

var newLang = {
    loadingBox:         '<div class="survey-loading" data-surveyjs-loading><i class="glyphicon glyphicon-refresh icon-spin"></i> Wird geladen...</div>',
    selectFirstOption:  'Wähle eine Antwort...',
    textareaPlaceholder:'Schreibe eine Antwort...',
    maxChoiceText:      'ANTWORTEN MAX',
    fieldErrorMessage:  'Du musst antworten',
    fieldErrorMessageMultiChoice:  'Sie Können bis zu {{maxChoiceNum}} Antworten auswählen.'
};

SURVEY.addLanguage( 'de', newLang );

Templates

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 Fields Error

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    fieldErrorTemplate: '<div class="field-error-message">{{fieldErrorMessage}}</div>';
});

Custom Template for Loading Box

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    loadingBox: '<div class="survey-loading" data-surveyjs-loading><i class="glyphicon glyphicon-refresh icon-spin"></i> Loading...</div>';
});

Custom Template for Questions

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    questionTemplate:   '<div data-question-id="{{questionId}}" data-question-index="{{questionNumber}}" data-formjs-question="" class="question-box clearfix" {{maxChoice}}>'+
                            '<div class="question-header">Question {{questionNumber}}</div>'+
                            '<div class="question-body">'+
                                '<div class="question-text">{{questionText}}</div>'+
                                '<div class="answers-box form-group clearfix">'+
                                    '{{answersHtml}}'+
                                '</div>'+
                            '</div>'+
                        '</div>';
});

Custom Template for Label Tags

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    labelTagCode:       '<label for="{{answerCode}}">{{answerString}}</label>',
});

Custom Template for Textareas

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    textareaTemplate:   '<div class="row">'+
                            '<div class="col-xs-12 textarea-box">'+
                                '<textarea id="{{answerCode}}" data-answer-id="{{answerId}}" {{nestedAnswer}} name="user-text-answer-{{questionNumber}}" {{attrRequired}} class="form-control" maxlength="250" rows="6" placeholder="{{placeholder}}"></textarea>'+
                            '</div>'+
                        '</div>';
});

Custom Template for Select Tags

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    selectTagCode:      '<select id="{{answerCode}}" name="survey-answer-{{questionNumber}}{{addMoreName}}" class="{{fieldClass}}" {{attrRequired}} {{nestedAnswer}} data-answer-root="{{progIdsJoined}}" {{attrRequiredFrom}}>'+
                            '{{optionsHtml}}'+
                        '</select>';
});

Custom Template for Selects

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    selectTemplate:     '<div class="single-answer" data-answer-index="{{answerIndex}}">'+
                            '{{selectTagCode}}'+
                        '</div>';
});

Custom Template for Input Tags

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    inputTagCode:       '<input type="{{answerType}}" name="survey-answer-{{questionNumber}}{{addMoreName}}" class="{{fieldClass}}" id="{{answerCode}}" {{nestedAnswer}} data-answer-root="{{progIdsJoined}}" data-answer-id="{{answerId}}" value="{{answerIdValue}}" {{attrRequired}} {{attrRequiredFrom}}/>';
});

Custom Template for Inputs

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    inputTemplate:      '<div class="single-answer input-container {{answerType}}" data-answer-index="{{answerIndex}}">'+
                            '{{inputLabelTagCode}}'+
                        '</div>';
});

Custom Template for Input Groups

SURVEY.setup({
    setJSONurl: 'json/survey.json',
    inputGroupTemplate: '<div class="single-answer input-group" data-answer-index="{{answerIndex}}">'+
                            '<span class="input-group-addon {{inputTypeClass}}">'+
                                '<input id="{{answerCode}}" type="{{answerType}}" data-answer-id="{{answerId}}" name="survey-answer-{{questionNumber}}" value="{{answerIdValue}}" {{attrRequired}} data-require-more=""/>'+
                                    '<label for="{{answerCode}}"> {{answerString}}</label>'+
                            '</span>'+
                            '{{relatedAnswerField}}'+
                        '</div>';
});