Hook jQuery validation message changes

2019-07-20 07:23发布

问题:

I want to display my jQuery validation messages in a tooltip. In order to accomplish this, I started out by adding the following CSS rules to my stylesheet:

fieldset .field-validation-error {
    display: none;
}

fieldset .field-validation-error.tooltip-icon {
    background-image: url('/content/images/icons.png');
    background-position: -32px -192px;
    width: 16px;
    height: 16px;
    display: inline-block;
}

and a very small piece of JS code:

; (function ($) {
    $(function() {
        var fields = $("fieldset .field-validation-valid, fieldset .field-validation-error");
        fields.each(function() {
            var self = $(this);
            self.addClass("tooltip-icon");
            self.attr("rel", "tooltip");
            self.attr("title", self.text());
            self.text("");
            self.tooltip();
        });
    });
})(jQuery);

The issue is that I now need to capture any event when the validation message changes, I've been looking at the source for jquery.validate.unobtrusive.js, and the method I'd need to hook to is the function onError(error, inputElement) method.

My tooltip plugin works as long as I've an updated title attribute, the issue comes when the field is revalidated, and the validation message is regenerated, I would need to hook into that and prevent the message from being put out there and place it in the title attribute instead.

I want to figure out a way to do this without modifying the actual jquery.validate.unobtrusive.js file.

On a second note, how could I improve this in order to leave the functionality unaltered in case javascript is disabled?

回答1:

Ok I went with this, just in case anyone runs into this again:

; (function ($) {
    $(function() {
        function convertValidationMessagesToTooltips(form) {
            var fields = $("fieldset .field-validation-valid, fieldset .field-validation-error", form);
            fields.each(function() {
                var self = $(this);
                self.addClass("tooltip-icon");
                self.attr("rel", "tooltip");
                self.attr("title", self.text());
                var span = self.find("span");
                if (span.length) {
                    span.text("");
                } else {
                    self.text("");
                }
                self.tooltip();
            });
        }

        $("form").each(function() {
            var form = $(this);
            var settings = form.data("validator").settings;
            var old_error_placement = settings.errorPlacement;
            var new_error_placement = function() {
                old_error_placement.apply(settings, arguments);
                convertValidationMessagesToTooltips(form);
            };
            settings.errorPlacement = new_error_placement;
            convertValidationMessagesToTooltips(form); // initialize in case of model-drawn validation messages at page render time.
        });
    });
})(jQuery);

and styles:

fieldset .field-validation-error { /* noscript */
    display: block;
    margin-bottom: 20px;
}

fieldset .field-validation-error.tooltip-icon { /* javascript enabled */
    display: inline-block;
    margin-bottom: 0px;

    background-image: url('/content/images/icons.png');
    background-position: -32px -192px;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}

I'll just include the tooltip script I have, since it's kind of custom-made (though I based it off someone else's).

; (function ($, window) {
    $.fn.tooltip = function (){
        var classes = {
            tooltip: "tooltip",
            top: "tooltip-top",
            left: "tooltip-left",
            right: "tooltip-right"
        };

        function init(self, tooltip) {
            if ($(window).width() < tooltip.outerWidth() * 1.5) {
                tooltip.css("max-width", $(window).width() / 2);
            } else {
                tooltip.css("max-width", 340);
            }

            var pos = {
                x: self.offset().left + (self.outerWidth() / 2) - (tooltip.outerWidth() / 2),
                y: self.offset().top - tooltip.outerHeight() - 20
            };

            if (pos.x < 0) {
                pos.x = self.offset().left + self.outerWidth() / 2 - 20;
                tooltip.addClass(classes.left);
            } else {
                tooltip.removeClass(classes.left);
            }

            if (pos.x + tooltip.outerWidth() > $(window).width()) {
                pos.x = self.offset().left - tooltip.outerWidth() + self.outerWidth() / 2 + 20;
                tooltip.addClass(classes.right);
            } else {
                tooltip.removeClass(classes.right);
            }

            if (pos.y < 0) {
                pos.y = self.offset().top + self.outerHeight();
                tooltip.addClass(classes.top);
            } else {
                tooltip.removeClass(classes.top);
            }

            tooltip.css({
                left: pos.x,
                top: pos.y
            }).animate({
                top: "+=10",
                opacity: 1
            }, 50);
        };

        function activate() {
            var self = $(this);
            var message = self.attr("title");
            var tooltip = $("<div class='{0}'></div>".format(classes.tooltip));

            if (!message) {
                return;
            }
            self.removeAttr("title");
            tooltip.css("opacity", 0).html(message).appendTo("body");

            var reload = function() { // respec tooltip's size and position.
                init(self, tooltip);
            };
            reload();
            $(window).resize(reload);

            var remove = function () {
                tooltip.animate({
                    top: "-=10",
                    opacity: 0
                }, 50, function() {
                    $(this).remove();
                });

                self.attr("title", message);
            };

            self.bind("mouseleave", remove);
            tooltip.bind("click", remove);
        };

        return this.each(function () {
            var self = $(this);
            self.bind("mouseenter", activate);
        });
    };

    $.tooltip = function() {
        return $("[rel~=tooltip]").tooltip();
    };
})(jQuery, window);