Плагин JQuery Validation: вызывать функцию errorPlacement, когда onfocusout, keyup и click

Я использую плагин проверки jquery и хочу использовать функцию errorPlacement для добавления сообщений об ошибках в атрибут заголовка полей и отображения только рядом с полем.

Это отлично работает, когда форма отправляется с помощью кнопки отправки, но при срабатывании любого из следующих событий:  - onfocusout  - щелкните  - onkeyup

Проверки проверки выполняются, но он пропускает функцию errorPlacement и добавляет полное сообщение об ошибке после поля, например поведение по умолчанию.

Я использую следующий код:

$("#send-mail").validate({
    debug: true,
    // set this class to error-labels to indicate valid fields 
    success: function(label) {
        // set text as tick
        label.html("✔").addClass("valid"); 
    }, 
     // the errorPlacement has to take the table layout into account 
    errorPlacement: function(error, element) {
        console.log("errorPlacement called for "+element.attr("name")+" field");
        // check for blank/success error
        if(error.text() == "")
        { 
            // remove field title/error message from element
            element.attr("title", "");
            console.log("error check passed");
        }
        else
        {
            // get error message
            var message = error.text();

            // set as element title
            element.attr("title", message);

            // clear error html and add cross glyph
            error.html("✘"); 

            console.log("error check failed: "+message);
        }
        // add error label after form element
        error.insertAfter(element);
    },
    ignoreTitle: true,
    errorClass: "invalid"
});

Ответ 1

Ваша проблема в том, что плагин только вызывает функцию errorPlacement один раз для каждого проверяемого элемента. Namly, когда сначала создается метка ошибки для элемента. После этого плагин просто повторно использует уже существующую метку и просто заменяет html внутри (или скрывает метку ошибки, если этот элемент теперь действителен). Вот почему ваш крест удаляется и отображается фактическое сообщение об ошибке.

Просто убедитесь, что поток плагина понятен.

  • элемент (еще нет errorlabel) Элемент
  • получает подтверждение в какой-то момент
  • плагин создает метку ошибки и вызывает errorPlacement функцию
  • элемент "cross" (сообщение об ошибке в заголовке)
  • Элемент получает фокус, и вы что-то меняете.
  • плагин обновляет элемент
  • Видит, что метка ошибки уже создана (и помещена)
  • плагин просто вызывает label.html(message) вместо удаления старой метки и чтения ее

Итак, вы видите, что ваша проблема - это своего рода оптимизация, которую плагин делает для сохранения ненужных вставок/удалений меток ошибок. Это тоже имеет смысл.

Вы можете проверить, что я сказал, посмотрев код validation-plugin-source

jquery.validate.js v1.6 проверить функции showLabel строки 617-625 для соответствующих фрагментов.


Возможное решение может заключаться в дополнительном предоставлении пользовательского обратного вызова showErrors, который решает проблему с грубой силой.

Что-то вдоль линий

$("#send-mail").validate({
...
    showErrors: function(errorMap, errorList) {
        for (var i = 0; errorList[i]; i++) {
            var element = this.errorList[i].element;
            //solves the problem with brute force
            //remove existing error label and thus force plugin to recreate it
            //recreation == call to errorplacement function
            this.errorsFor(element).remove();
        }
        this.defaultShowErrors();
    }
...
});

Возможно, для этого есть более чистое решение, но это должно сделать это и дать вам время для изучения лучшего решения.

Ответ 2

Спасибо дрожание,

Я сделал кое-какие операции и нашел ту же проблему.

Мне удалось заставить его работать, "взломав" функцию showLabel в jquery.validation.js. Это не очень, но работает.

Переопределение опции функции showErrors не позволит мне изменить код плагина, чтобы я посмотрел.

Вот код, который я использовал для метода showLabel:

     showLabel: function(element, message) {

            // look for existing error message
            var label = this.errorsFor( element );
            // existing error exist?
            if (label.length) {
                // refresh error/success class
                label.removeClass().addClass( this.settings.errorClass );

                // check if we have a generated label, replace the message then
                label.attr("generated");

                // is message empty?
                if(!message)
                {
                    // add tick glyph
                    label.html("✔");

                    // wipe element title
                    $(element).attr('title', message)
                }
                else
                {
                    // clear error html and add cross glyph
                    label.html("✘");

                    // update element title
                    $(element).attr('title', message)
                }

                // && label.html(message);
            } 
            else {
                // create label
                label = $("<" + this.settings.errorElement + "/>")
                    .attr({"for":  this.idOrName(element), generated: true})
                    .addClass(this.settings.errorClass)
                    .html(message || "");
                if ( this.settings.wrapper ) {
                    // make sure the element is visible, even in IE
                    // actually showing the wrapped element is handled elsewhere
                    label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
                }
                if ( !this.labelContainer.append(label).length )
                    this.settings.errorPlacement
                        ? this.settings.errorPlacement(label, $(element) )
                        : label.insertAfter(element);
            }
            if ( !message && this.settings.success ) {
                label.text("");
                typeof this.settings.success == "string"
                    ? label.addClass( this.settings.success )
                    : this.settings.success( label );
            }
            this.toShow = this.toShow.add(label);
        }