Search code examples
javascriptjquerybootbox

Bootbox incorrectly submits a form when a single input field is present


I have a strange problem that's really starting to bug me. Apologies in advance for a wall of code and somewhat confusing question.

I need to display a modal form for the user, and have them fill in some details.

  • The user can click Save to save their changes.
  • The user can click Cancel to cancel their changes.
  • I use the save handler to serialize the form and send its data to a JSON service.

If I have a form with multiple input fields, it all works great, and nothing unexpected happens.

If I have a form with a single input field, however, I get an unexpected side-effect. Hitting Enter/Return in that input field causes the modal form to be submitted, and instead of my JSON handler getting called the page is reload with the form's arguments as parameters — exactly as if the form is being submitted. In fact, adding an action= parameter to the form element has proven that, as you get navigated to the page you specify.

Here's the form I'm using:

<form id="surveyQuestionForm" class="form-horizontal" style="display:none;">
    <div class="row">
        <input name="surveyQuestionId" id="surveyQuestionId" type="hidden">
        <input name="surveyId" type="hidden" value="${survey.surveyId}">
        <div class="control-group">
            <label class="control-label" for="questionType"><b><spring:message code="survey.question.type"/></b></label>
            <div class="controls">
                <select class="input-large" name="questionType" id="questionType">
                    <option value="">(Select one)</option>
                    <c:forEach items="${surveyQuestionTypes}" var="surveyQuestionType">
                        <option value="${surveyQuestionType.code}">${surveyQuestionType.name}</option>
                    </c:forEach>
                </select>
            </div>
        </div>
        <div class="control-group">
            <label class="control-label" for="questionText"><b><spring:message code="survey.question.text"/></b></label>
            <div class="controls">
                <input type="text" class="input-xlarge" name="questionText" id="questionText" maxLength="64"/>
            </div>
        </div>
    </div>
</form>

and here's the code I use to display the form modally:

function addQuestion() {

    // find the form, and initialise its validation.
    var form = $('#surveyQuestionForm');
    var validator = form.validate(
        {
            rules: {
                questionType: {
                    required: true
                },
                questionText: {
                    required: true
                }
            },
            messages: {
                questionType: {
                    required: '<spring:message javaScriptEscape="true" code="survey.question.type.required"/>'
                },
                questionText: {
                    required: '<spring:message javaScriptEscape="true" code="survey.question.text.required"/>'
                }
            },
            onkeyup: false
        });

    // reset form validation, and hide any error message
    validator.resetForm();
    $("#errorMessage").hide();

    // show the dialog
    bootbox.dialog({
            title: '<i class="icon-plus green"/>&emsp;<spring:message javaScriptEscape="true" code="survey.add.question"/>',
            message: form,
            closeButton: false,
            buttons: {
                cancel: {
                    label: '<i class="icon-remove bigger-130"></i> <spring:message javaScriptEscape="true" code="button.cancel"/>',
                    className: "btn btn-danger"
                },
                save: {
                    label: '<i class="icon-ok bigger-130"></i> <spring:message javaScriptEscape="true" code="button.save"/>',
                    className: 'btn btn-success',
                    callback: function () {
                        var result = false;
                        if (!form.valid())
                            return false;
                        $.ajax({
                                type: "POST",
                                url: '/addSurveyQuestion.json',
                                async: false,
                                data: form.serialize(),
                                success: function (outcome) {
                                    if (outcome.success) {
                                        $('#question-list').dataTable().fnReloadAjax();
                                        result = true;
                                    }
                                    else {
                                        $("#errorMessage").html(htmlEncode(outcome.message)).show();
                                    }
                                }
                            }
                        ).fail(function () {
                                $.gritter.add({
                                        title: '<spring:message javaScriptEscape="true" code="general.error"/>',
                                        text: '<spring:message javaScriptEscape="true" code="server.error"/>',
                                        class_name: 'gritter-error'
                                    }
                                );
                            }
                        );
                        return result;
                    }
                }
            },
            show: false,
            animate: false,
            onEscape: false
        }
    ).on('shown.bs.modal', function () {
            var form = $('#surveyQuestionForm');
            form.find('#surveyQuestionId').val(null);
            form.find('#questionType').val('');
            form.find('#questionText').val('');
            form.show().find('#questionType').focus();
            form.show();
        }
    ).on('hide.bs.modal', function (e) {
            if (e.target === this)
                $('#surveyQuestionForm').hide().appendTo('body');
        }
    ).modal('show').addClass("bootboxDialog40");
}

If I use this code as-is, with Bootbox 4.4, hitting Enter/Return while the user is in the questionText field submits the form, and my page redisplays but with the form fields as parameters, eg:

page.html?surveyQuestionId=&surveyId=3&questionType=Y&questionText=blah

If I have a second input field, hitting Enter/Return in the fields does nothing, and the user has to click Save or Cancel.


Solution

  • Submit-on-enter for a single input field is a browser behavior that you will need to override. You can do this a few ways.

    <form onSubmit="return false;">
    

    I don't think you are using the native submit function at all, so adding this bit of inline scripting prevents the form submission. But putting scripts in your markup isn't great. A little jQuery can do the same thing for you:

    $('form').on('submit', function(){ return false; });