Search code examples
jqueryvalidationtinymcetooltip

jQuery form validation with tinyMCE and nice tooltip / focus / error indication


I try to use the jQuery validation plugin together with tinyMCE. What I would like to do is following:

  • On submit the form shall be validated. Invalid controls shall be highlighted (with .error CSS class) and a tooltip shall inform about the error.
  • Whenever the user fixes the error (e.g. by typing in the correct value), the tooltip and the .error highlight shall disappear.
  • Whenever an error occurs, the first element in the form shall be focused.

The tinyMCE / validation integration example already helped me a lot! But there is still an issue with updating the error state - especially for the tinyMCE form.

I've prepared a running fiddle.

Update

After some further investigation I found out, that in the "success" method, the "label" of the control (e.g. the textarea) is returned, not the control itself. See the console output. But I would need the control - any ideas how to do that? is that the correct approach how I do it?

Here is (only) the javascript (the whole example does not run here).

/// jQuery Validator Defaults
$.validator.setDefaults({
  errorPlacement: function(error, obj) {
    console.log("invalid: id   - " + obj.attr('id'));
    console.log("invalid: type - " + obj.prop('nodeName'));

    var myobj = obj;
    if (obj.is("textarea")) {
      myobj = obj.prev().contents().find('iframe');
      obj.prev().addClass('error');
    }
    myobj.attr('title', error.text());
    var errorLocation = $(".error");
    errorLocation.tooltip({
      position: {
        my: "center bottom",
        at: "center top+8"
      }
    });
  },
  success: function(obj) {
    console.log("  valid: id   - " + obj.attr('id'));
    console.log("  valid: type - " + obj.prop('nodeName'));

    var myobj = obj;
    if (obj.is("textarea")) {
      myobj = obj.prev().contents().find('iframe');
      obj.prev().removeClass('error');
    }
    myobj.removeClass("error").attr("title", "");
  },
  focusInvalid: function(obj) {
    // put focus on tinymce on submit validation
    if (obj.settings.focusInvalid) {
      try {
        var toFocus = $(obj.findLastActive() || obj.errorList.length && obj.errorList[0].element || []);
        if (toFocus.is("textarea")) {
          tinyMCE.get(toFocus.attr("id")).focus();
        } else {
          toFocus.filter(":visible").focus();
        }
      } catch (e) {
        // ignore IE throwing errors when focusing hidden elements
      }
    }
  }
});

function initMCE() {
  $("textarea.tinymce").tinymce({
    theme: "modern",
    toolbar_items_size: "small",
    toolbar: "bold italic underline strikethrough | alignleft aligncenter alignright alignjustify table | copy paste | bullist numlist | undo redo | link fullscreen localautosave ",
    setup: function(editor) {
      editor.on("change", function(e) {
        //console.log("change event", e);
        tinyMCE.triggerSave();
        $("#" + editor.id).valid();
      });
    }
  });
}

$(function() {

  initMCE();

  var validator = $("#myform").submit(function() {
    // update underlying textarea before submit validation
    tinyMCE.triggerSave();
  }).validate({
    ignore: "",
    rules: {
      title: "required",
      someoptions: "required",
      content: "required"
    },
    messages: {
      title: "Please enter title",
      someoptions: "Please choose option",
      content: "Please enter some text"
    }
  });
});


Solution

  • Ok, after some further trials and errors I did it :)

    Here is the fiddle.

    As you can see I now use the highlight and unhighlight method of the validation plugin:

    /// jQuery Validator Defaults
    $.validator.setDefaults({
      errorPlacement: function(error, element) {
        var myelement = element;
    
        if (element.is("textarea")) {
          myelement = element.prev().contents().find('iframe');
        }
    
        myelement.attr('title', error.text());
        var errorLocation = $(".error");
        errorLocation.tooltip({
          position: {
            my: "center bottom",
            at: "center top+8"
          }
        });
      },
    
      highlight: function(element, errorClass, validClass) {
        $(element).addClass(errorClass).removeClass(validClass);
    
        if ($(element).is("textarea")) {
          $(element).prev().addClass('error');
        }
      },
    
      unhighlight: function(element, errorClass, validClass) {
        $(element).removeClass(errorClass).addClass(validClass);
    
        if ($(element).is("textarea")) {
          $(element).prev().removeClass('error');
        }
      },
    
      success: function(element) {
        var myelement = element;
        if (element.is("textarea")) {
          myelement = element.prev().contents().find('iframe');
          //obj.prev().removeClass('error');
        }
        myelement.attr("title", "");
      }
    });
    
    function initMCE() {
      $("textarea.tinymce").tinymce({
        theme: "modern",
        toolbar_items_size: "small",
        toolbar: "bold italic underline strikethrough | alignleft aligncenter alignright alignjustify table | copy paste | bullist numlist | undo redo | link fullscreen localautosave ",
        setup: function(editor) {
          editor.on("change", function(e) {
            //console.log("change event", e);
            tinyMCE.triggerSave();
            $("#" + editor.id).valid();
          });
        }
      });
    }
    
    $(function() {
    
      initMCE();
    
      var validator = $("#myform").submit(function() {
        // update underlying textarea before submit validation
        tinyMCE.triggerSave();
      }).validate({
        debug: true,
        ignore: "",
        rules: {
          title: "required",
          someoptions: "required",
          content: "required"
        },
        messages: {
          title: "Please enter title",
          someoptions: "Please choose option",
          content: "Please enter some text"
        }
      });
    
      validator.focusInvalid = function() {
        // put focus on tinymce on submit validation
        if (this.settings.focusInvalid) {
          try {
            var toFocus = $(this.findLastActive() || this.errorList.length && this.errorList[0].element || []);
            if (toFocus.is("textarea")) {
              tinyMCE.get(toFocus.attr("id")).focus();
            } else {
              toFocus.filter(":visible").focus();
            }
          } catch (e) {
            // ignore IE throwing errors when focusing hidden elements
          }
        }
      }
    });