简体   繁体   中英

jQuery Image Validation For Dynamically Cloned Form

I have a form in my HTML that will get cloned and appended dynamically when the user hits the #addOne button. The form gets validated successfully for any input errors, the only problem that I am facing right now is that it's not working correctly for images. Let's say I upload an image for the first form, it works perfect. But when I click the #addOne button and upload an image for the second form, that's when the problems arise. Prior to even uploading an image for the second form, it's already displaying the image from the previous form on to the page. Uploading an image for this form, will not update form 2. Rather, it'll change form 1's displayed image. How can I make it so that each form displays it's own uploaded image and is validated properly? Here's my jsFiddle

HTML

<div class="article_properties">

  <form class="article_properties_form" action="" method="POST" enctype="multipart/form-data">
    <p style="display: inline">Page Number</p>
    <div style="background-color: #FF355E; padding: 5px; display: inline; margin-left: 5px">

      <p style="display: inline" class="pageNumber"></p>
    </div>
    <textarea style="display: none" class="inputNumber" name="pages"></textarea>
    <p>Image</p>
    <input style="padding: 0px" type="file" name="image" class="pageImg">
    <div class="imgContainer">
    </div>
    <p>Subtitle</p>
    <input type="text" name="subtitle">

    <p>Text</p>
    <textarea name="text" rows="4"></textarea>
    <input id="properties_btn" type="submit" value="Submit/Update" name="properties_submit">
    <hr style="border: 1px dotted lightgray; margin-bottom: 50px">
  </form>


  <a style="display: none; text-align: center; margin: 50px; font-size: 25px" class="expand" href="#">

  </a>
</div>
<!--End of article properties div-->
<div id="addOne">
  <p>+Add page</p>
</div>

<div class="nextBtn" style="display: none">
  <p>Finalize my article</p>
</div>

jQuery

var numPagesTemp = 4;
$('.pageNumber:last').text(numPagesTemp);
$('.inputNumber:last').text(numPagesTemp);
//Invoke functions for first form
add_validation_for_forms();
add_image_construction();

//Form validation
function add_validation_for_forms() {
  $(".article_properties_form").each(function() {
    $(this).validate({
      errorElement: 'div',

      rules: {
        image: {
          required: true,
          extension: "jpg|jpeg|png",
          minImageSize: {
            width: 600,
            height: 400
          }
        },

        subtitle: {
          required: true,
          minlength: 2,
          maxlength: 25
        },
        text: {
          required: true,
          minlength: 35,
          maxlength: 275
        }
      },

      messages: {
        image: {
          required: "This page needs an image",
          extension: "You're only allowed to upload jpg or png images."
        },

        subtitle: {
          required: "You have to provide a subtitle for this page!",
          minlength: "Your subtitle must be at least 2 characters long",
          maxlength: "Your subtitle must be less than 25 characters long"
        },
        text: {
          required: "Please enter text for this page",
          minlength: "Your text must be at least 35 characters long",
          maxlength: "Your text must be less than 275 characters long"
        },
      },
    });
  });
}
//Adding a form
$('#addOne').click(function() {

  numPagesTemp--;

  var articlePropsTemplate = $('.article_properties_form:last').clone();
  articlePropsTemplate.show();
  $('.article_properties').append(articlePropsTemplate);

  var articlePropsExpand = $('.expand:last').clone();
  articlePropsExpand.text("Expand " + numPagesTemp);
  articlePropsExpand.hide();

  $('.article_properties').append(articlePropsExpand);

  $('.pageNumber:last').text(numPagesTemp);
  $('.inputNumber:last').text(numPagesTemp);
  articlePropsTemplate[0].reset();
  add_validation_for_forms();

  add_image_construction();
  articlePropsTemplate.validate().resetForm();

  if (numPagesTemp == 1) {
    $('#addOne').hide();
    $(".nextBtn").show();
  }

});

//Adding Method
$.validator.addMethod('minImageSize', function(value, element, minSize) {
  var imageSize = $(element).data('imageSize');
  return (imageSize) && (imageSize.width >= minSize.width) && (imageSize.height >= minSize.height);
}, function(minSize, element) {
  return ($(element).data('imageSize')) ? ("Your image's size must be at least " + minSize.width + "px by " + minSize.height + "px") : "Selected file is not an image.";
});

//Image Uploading
var $properties_btn = $('properties_btn'),
  $imgContainer = $('.imgContainer'),
  $pageImg = $('.pageImg');

function add_image_construction() {

  $('.pageImg').change(function() {
    $pageImg.removeData('imageSize');
    $imgContainer.hide().empty();

    var file = this.files[0];

    if (file.type.match(/image\/.*/)) {
      $properties_btn.attr('disabled', true);

      var reader = new FileReader();

      reader.onload = function() {
        var $img = $('<img />').attr({
          src: reader.result
        });

        $img.on('load', function() {
          $imgContainer.append($img).show();

          $pageImg.data('imageSize', {
            width: $img.width(),
            height: $img.height()
          });

          $img.css({
            width: '400px',
            height: '200px'
          });

          $properties_btn.attr('disabled', false);

          validator.element($pageImg);
        });
      }

      reader.readAsDataURL(file);
    } else {
      validator.element($pageImg);
    }
  });
}

My advice when cloning is this:

Never clone elements that have been instrumented.

By instrumented, I mean they have JavaScript behavior set up on them, like event handling.

Instead do the following:

(1) Clone the elements before they have been instrumented and keep the clone in a variable.

var $CLONED_FORM = $('.article_properties_form:first').clone();

(2) Provide a function that will instrument the set of elements that are to be cloned.

function initializeForm($form, pageNum) {
  // Set up form validation here.
  // Also attach the change-event handler for the file input here.
}

(3) Call the function for the sets that are already on the page.

$('.article_properties_form').each(function() {
  initializeForm($(this), numPagesTemp--);
});

(4) When you want to add another set of the elements, clone the clone and add it, and call the function on it.

$('#addOne').click(function() {
  var $newForm = $CLONED_FORM.clone().appendTo('.article_properties').show();

  initializeForm($newForm, numPagesTemp--);

  if (numPagesTemp == 0) {
    $('#addOne').hide();
    $(".nextBtn").show();
  }
});

jsfiddle


You also need to store the validator object in a variable named validator .

var validator = $form.validate({

Also, you should avoid duplicate id values when cloning. You have an id on the form submit button, but it does not appear to be needed, so perhaps you can just remove that.

I need to use my code

Edit, Updated

Substituted class="properties_btn" for id="properties_btn" to prevent duplicate id being appended to document ; defined validator at change event.

 $(function() { var numPagesTemp = 4; $('.pageNumber:last').text(numPagesTemp); $('.inputNumber:last').text(numPagesTemp); //Invoke functions for first form add_validation_for_forms(); add_image_construction(); //Form validation function add_validation_for_forms() { $(".article_properties_form").each(function() { $(this).validate({ errorElement: 'div', rules: { image: { required: true, extension: "jpg|jpeg|png", minImageSize: { width: 600, height: 400 } }, subtitle: { required: true, minlength: 2, maxlength: 25 }, text: { required: true, minlength: 35, maxlength: 275 } }, messages: { image: { required: "This page needs an image", extension: "You're only allowed to upload jpg or png images." }, subtitle: { required: "You have to provide a subtitle for this page!", minlength: "Your subtitle must be at least 2 characters long", maxlength: "Your subtitle must be less than 25 characters long" }, text: { required: "Please enter text for this page", minlength: "Your text must be at least 35 characters long", maxlength: "Your text must be less than 275 characters long" }, }, }); }); } //Adding a form $('#addOne').click(function() { numPagesTemp--; var articlePropsTemplate = $('.article_properties_form:last') .clone(); articlePropsTemplate.find("img").remove(); $('.article_properties').append(articlePropsTemplate); var articlePropsExpand = $('.expand:last').clone(); articlePropsExpand.text("Expand " + numPagesTemp); articlePropsExpand.hide(); $('.article_properties').append(articlePropsExpand); $('.pageNumber:last').text(numPagesTemp); $('.inputNumber:last').text(numPagesTemp); $("form:last")[0].reset(); add_validation_for_forms(); // add_image_construction(); articlePropsTemplate.validate().resetForm(); if (numPagesTemp == 1) { $('#addOne').hide(); $(".nextBtn").show(); } }); //Adding Method $.validator.addMethod('minImageSize', function(value, element, minSize) { var imageSize = $(element).data('imageSize'); return (imageSize) && (imageSize.width >= minSize.width) && (imageSize.height >= minSize.height); }, function(minSize, element) { return ($(element).data('imageSize')) ? ("Your image's size must be at least " + minSize.width + "px by " + minSize.height + "px") : "Selected file is not an image."; }); //Image Uploading // var $properties_btn = $('.properties_btn'), // $imgContainer = $('.imgContainer'), // $pageImg = $('.pageImg'); function add_image_construction() { $(document).on("change", ".pageImg", function(e) { var form = $(this).closest("form"); var validator = form.validate(); $(this).removeData('imageSize'); form.find('.imgContainer').hide().empty(); var file = this.files[0]; if (file.type.match(/image\\/.*/)) { form.find('.properties_btn').attr('disabled', true); var reader = new FileReader(); reader.onload = function() { var $img = $('<img />').attr({ src: reader.result }); $img.on('load', function() { form.find('.imgContainer').append($img).show(); $(e.target).data('imageSize', { width: $img.width(), height: $img.height() }); $img.css({ width: '400px', height: '200px' }); form.find('.properties_btn').attr('disabled', false); validator.element(e.target); }); } reader.readAsDataURL(file); } else { validator.element(e.target); } }); } }) 
 form { border: 1px solid blue; padding: 10px; margin: 10px; } 
 <script src="https://code.jquery.com/jquery-3.0.0.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/additional-methods.min.js"></script> <div class="article_properties"> <form class="article_properties_form" action="" method="POST" enctype="multipart/form-data"> <p style="display: inline">Page Number</p> <div style="background-color: #FF355E; padding: 5px; display: inline; margin-left: 5px"> <p style="display: inline" class="pageNumber"></p> </div> <textarea style="display: none" class="inputNumber" name="pages"></textarea> <p>Image</p> <input style="padding: 0px" type="file" name="image" class="pageImg"> <div class="imgContainer"> </div> <p>Subtitle</p> <input type="text" name="subtitle"> <p>Text</p> <textarea name="text" rows="4"></textarea> <input class="properties_btn" type="submit" value="Submit/Update" name="properties_submit"> <hr style="border: 1px dotted lightgray; margin-bottom: 50px"> </form> <a style="display: none; text-align: center; margin: 50px; font-size: 25px" class="expand" href="#"> </a> </div> <!--End of article properties div--> <div id="addOne"> <p>+Add page</p> </div> <div class="nextBtn" style="display: none"> <p>Finalize my article</p> </div> 


This is a simplified version of what you are trying to achieve, without validating uploaded file

 // maximum number of `form` elements which `document` should contain var n = 4; // display `form` index function handleLabel() { $("label").html(function(index, html) { return "form #" + index }) } handleLabel(); function processFile() { // preview uploaded image $("<img>", {src: URL.createObjectURL(this.files[0])}) .insertAfter(this.nextElementSibling).after("<br>"); } // handle file upload; delegate event to `document` $(document).on("change", ":file", processFile); // append cloned `form` to page $("button").on("click", function() { // if less than `n` forms if ($("form").length < 4) { // copy last `form` `.outerHTML` instead of using `.clone()` $($("form:last")[0].outerHTML) // remove `img` from `html` .find("img").remove().end() .insertAfter("form:last"); // update `form` `label` elements handleLabel(); } else { // detach `click` event from `button` // when four `form` elements exist in `document` $(this).off("click") } }); // handle `form` submit event $(document).on("submit", "form", function(e) { var img = $(this).find("img")[0]; // check `img` `width`, `height` if (img.naturalWidth < 600 || img.naturalHeight < 400) { e.preventDefault(); this.reset(); alert("invalid img width or height") } }) 
 form { border: 1px solid blue; padding: 10px; margin: 10px; } 
 <script src="https://code.jquery.com/jquery-3.0.0.js"></script> <form> <fieldset> <label></label><br> <input name="file" type="file" accept=".jpg,.jpeg,.png" required /> <br> <input type="submit" /> </fieldset> </form> <button> add form </button> 

jsfiddle https://jsfiddle.net/hLjvffpv/5/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM