3
\$\begingroup\$

This code is not live, but for brevity I'll write as though it is.

When a user clicks any element with the class name js-modalOpen, for example:

<span class="js-modalOpen" data-modal="signup">Sign up</span>

A modal appears. The contents of that modal's dialog are determined by data-modal. All modal contents live as snippets in my framework's static/html/ directory. My first concern is that I'm calling the methods and then binding their behavior to the target elements. Somehow it makes sense to me, but I've rarely seen it in production. Thus:

Modal.modalOpenListener();

Would call

var Modal = (function ($, SCRIPT_ROOT, CSRF_TOKEN) {

methods = {};

methods.modalOpenListener = function() {
    $(".js-modalOpen").on("click", function() {
        // Some names for loading snippets
        var snippetType = $(this).attr("data-modal");
        var snippet     = SCRIPT_ROOT + "/static/html/" + snippetType + "_modal_snippet.html";
        $(".modal").toggle();
        $(".modal").addClass("visible");
        document.body.style.overflow = "hidden";
        dialogContentsLoad(snippetType, snippet);
    });
}

return methods;

})(jQuery, SCRIPT_ROOT, CSRF_TOKEN);

The general structure of the code follows this pattern within the module. I'm concerned chiefly about

  • Security (I'm bad at this)
  • Modularity

I'll paste the whole code base I currently have for the sake of anyone brave enough to wade through it.

var Modal = (function ($, SCRIPT_ROOT, CSRF_TOKEN) {

    methods = {};

    // TODO:
    // 1. Write a function that gathers the values of the input
    // so that if the user accidentally closes the modal, the
    // values remain when it's reopened.
    // 2. Write a function the stops the ability to tab into
    // the main web page when the modal is open
    // 3. If a given form field IS NOT ".js-required" and IS empty,
    // don't serialize that part, so it gets ignored in error
    // checking

    var dialogContentsLoad = function(snippetType, snippet) {
        $(".dialogContents").load(snippet, function() {
            // If there's a form in the dialog, do this (".length" because
            // jQuery will simply return an empty array if there's not matching
            // element)
            if ($(".dialog").find("form").length) {
                var formType = snippetType;
                var form     = formType + "Form";
                // All modal fields will have a tabindex of "0"
                $("input").first().attr("tabindex", "0").focus();
                $("input[name='_csrf_token']").val(CSRF_TOKEN);
                // This should probably be taken out of this function...right?
                $("input").on("focus", function() {
                    $(this).css("background-color", "#2b2b2b")
                });
                $("input").on("blur", function() {
                    $(this).css("background-color", "#4b4b4b")
                });
                formSubmitListener(form, formType, CSRF_TOKEN);
            }
        });
    }

    var formSubmitListener = function(form, formType, CSRF_TOKEN) {
        var destinationURL = SCRIPT_ROOT + "/" + formType + "_action";
        $("#" + form).bind("submit", function(e) {
            e.preventDefault();
            $.ajax({
                type: "POST",
                data: $("#" + form).serialize(),
                url: destinationURL,
                success: function(resp) {
                    if (resp != "0") {
                        errors = $.parseJSON(resp);
                        console.log(errors['emptyFields']);
                        $("." + form + "Errors").empty();
                        if (!$.isEmptyObject(errors.emptyFields)) {
                            $("." + form + "Errors").append("Please fill out all required fields.")
                            for (error in errors.emptyFields) {
                                $("input[name='" + errors.emptyFields[error] + "Signup']").css("background-color", "#f22166");
                            }
                        }
                        delete errors.emptyFields;
                        console.log(errors);
                        for (error in errors) {
                            $("." + form + "Errors").append(errors[error]);
                        }
                    } else {
                            location.reload(false);
                    }
                }
            });
        });
    }

    methods.modalOpenListener = function() {
        $(".js-modalOpen").on("click", function() {
            var snippetType = $(this).attr("data-modal");
            var snippet     = SCRIPT_ROOT + "/static/html/" + snippetType + "_modal_snippet.html";
            $(".modal").toggle();
            $(".modal").addClass("visible");
            document.body.style.overflow = "hidden";
            dialogContentsLoad(snippetType, snippet);
        });
    }

    methods.modalCloseListener = function() {
        $(document).on("keyup", function(e) {
            if (e.keyCode == 27 && $(".modal").hasClass("visible")) {
                $(".modal").toggle();
                $(".modal").removeClass("visible");
                document.body.style.overflow = "visible";
            }
        });
        $(".js-modalClose").on("click", function(e) {
            if (e.target == this) {
                $(".modal").toggle();
                $(".modal").removeClass("visible");
                document.body.style.overflow = "visible";
            }
        });
    }

    return methods;

})(jQuery, SCRIPT_ROOT, CSRF_TOKEN);

I appreciate any input I can get on this.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

From a once over:

  • // This should probably be taken out of this function...right? <- Yes, this should be in css
  • var formSubmitListener = function(form, formType, CSRF_TOKEN) <- Asking for CSRF_TOKEN is superfluous, you already have access to it via
    var Modal = (function ($, SCRIPT_ROOT, CSRF_TOKEN) {
  • console.log should be avoided in production code
  • Whereas on the whole your code seems re-usable, this particular seems really specific and not re-usable:
    $("input[name='" + errors.emptyFields[error] +"Signup']").css("background-color", "#f22166");
  • You copy pasted this a number of times, use a helper function:

    $(".modal").toggle();
    $(".modal").addClass("visible");
    document.body.style.overflow = "hidden";
    
  • I would place the // TODO: above the var Modal
  • location.reload(false); <- This is antithetical to AJAX, why do AJAX and then refresh the whole page anyway on success? It does not make sense to me.
  • I think it is okay to be very specific in building the location of your script URL:
    var snippet = SCRIPT_ROOT + "/static/html/" + snippetType + "_modal_snippet.html"; but you have to be cognizant that you are coupling front and back end really tightly here.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.