Home » Jquery » javascript – Multiple dropzones on same page cause bugs to appear-Exceptionshub

javascript – Multiple dropzones on same page cause bugs to appear-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

I have multiple dropzones (dynamic, each product represents a product that is added in a basket in my webshop).

Every image upload an ajax call is made that returns info of the uploaded image and displays it in the previewtemplate (thumbnail block of the uploaded image). Info like the dimensions, DPI and filename.

The problem is that info is always displayed only in the last dropzone, in the last previewtemplate.

For example:

When I have two dropzones.
I upload an image of 20DPI in the first one, result: no information is shown in the preview.

I upload an image of 20DPI in the second one, result: the preview is shown with 20 DPI as information which is correct.

I upload two images at the same time in the second dropzone, the first image with 20DPI the second one with 72DPI, result: the first preview is empty and the last preview briefly shows 20DPI before it is overwritten with 72DPI.

This means the information is received correctly, it’s just not placed correctly in the previewtemplates.

This is my dropzone code, it loops through all .dropzone classes:

$('.dropzone').each(function(index){
    $maxfiles = $(this).attr('maxfiles');
    $inputquantity = $(this).find('input').val();
    $thisdropzone = $(this);
    $(this).dropzone({
        paramName: 'postedFile',
        addRemoveLinks: true,
        dictDefaultMessage: '<i class="fas fa-file-upload uploadicon"></i> <span class="uploadtxt">Upload je bestand(en)</span>',
        dictRemoveFile: 'Verwijder',
        dictCancelUpload: 'Annuleren',
        dictInvalidFileType: 'Dit type bestand is niet toegestaan',
        dictCancelUploadConfirmation: 'Weet je zeker dat je het uploaden wilt annuleren?',
        dictMaxFilesExceeded: 'Maximale aantal bestanden overschreden',
        maxFiles: $maxfiles,
        acceptedFiles: '.jpg, .jpeg, .png, .pdf, .tif, .tiff',
        thumbnailWidth: '205',
        thumbnailHeight: '140',
        thumbnailMethod: 'crop',
        previewTemplate: $(".hiddendiv").html(),
        sending: function (file) {

        },
        // File contains dropzone file object, response contains ajax response from php file
        success: function (file, response) {
            var obj = JSON.parse(response);
            $($thisdropzone).find('.dz-preview:last .bestandnaam').text('Bestandsnaam: ' + obj[0].filename);
            $($thisdropzone).find('.dz-preview:last .resolutie').text('Resolutie: ' + obj[0].dpi + ' DPI');
            $($thisdropzone).find('.dz-preview:last .formaat').text('Formaat: ' + obj[0].heightcm + ' x ' + obj[0].widthcm + 'cm');
        },
    })
});

And this is my html with in this example two dropzones (the forms):

<table class="table upload-table">
   <tbody>
      <tr>
         <td class="plantmore-product-thumbnail uploadimg" width="100">
            <a href=""><img src="assets/images/noimg.jpg" alt=""></a>
         </td>
         <td class="plantmore-product-name" width="200">
            <div class="prodinfocheckout">
               <a class="prodname" href="">
               Monomeer
               </a>
               <span id="togglespecscheckout" class="prodspecscheckout noselect">
               <i class="fas fa-chevron-down"></i> Specificaties
               </span>
               <div class="togglespecscheckout">
                  Hoogte : 20cm
                  <br>
                  Breedte : 20cm
                  <br>
                  uploaden : 1
                  <br>
                  Lijmlaag : Wit
                  <br>
                  Laminaat : Anti-slip laminaat
                  <br>
                  Afwerking : Contoursnijden
                  <br>
               </div>
            </div>
         </td>
         <td class="plantmore-product-quantity" width="190">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">
            Benodigd formaat:<br>
            <span class="benodigd">20 x 20cm</span>
            </span>
            </span>
         </td>
         <td class="plantmore-product-quantity" width="185">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">Benodigde aantal<br> bestanden: <span class="benodigd">10</span></span>
            </span>
         </td>
         <td class="plantmore-product-quantity">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">Bestanden <br>toegewezen: <span class="benodigd">0 / 10</span></span>
            </span>
         </td>
         <td class="plantmore-product-quantity" valign="top">
            <button class="uploadbutton btn yellowbtn dz-clickable">Bestand(en) uploaden</button>
         </td>
      </tr>
      <tr class="newrow">
         <td colspan="6">
            <form action="upload/uploaden.php" class="dropzone dropzoneform dz-clickable" maxfiles="10" id="dropzone1">
               <input type="hidden" value="Monomeer" name="productnaam">
               <input type="hidden" value="Twan" name="klantnaam">
               <input type="hidden" value="20" name="hoogte">
               <input type="hidden" value="20" name="breedte">
               <div class="dz-default dz-message"><span><i class="fas fa-file-upload uploadicon"></i> <span class="uploadtxt">Upload je bestand(en)</span></span></div>
            </form>
         </td>
      </tr>
      <tr>
         <td class="plantmore-product-thumbnail uploadimg" width="100">
            <a href=""><img src="assets/images/noimg.jpg" alt=""></a>
         </td>
         <td class="plantmore-product-name" width="200">
            <div class="prodinfocheckout">
               <a class="prodname" href="">
               Monomeer
               </a>
               <span id="togglespecscheckout" class="prodspecscheckout noselect">
               <i class="fas fa-chevron-down"></i> Specificaties
               </span>
               <div class="togglespecscheckout">
                  Hoogte : 90cm
                  <br>
                  Breedte : 90cm
                  <br>
                  uploaden : 1
                  <br>
                  Lijmlaag : Wit
                  <br>
                  Laminaat : Anti-slip laminaat
                  <br>
                  Afwerking : Contoursnijden
                  <br>
               </div>
            </div>
         </td>
         <td class="plantmore-product-quantity" width="190">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">
            Benodigd formaat:<br>
            <span class="benodigd">90 x 90cm</span>
            </span>
            </span>
         </td>
         <td class="plantmore-product-quantity" width="185">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">Benodigde aantal<br> bestanden: <span class="benodigd">1</span></span>
            </span>
         </td>
         <td class="plantmore-product-quantity">
            <span class="centervertical">
            <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
            <i class="fas fa-info-circle"></i>
            </button>
            <span class="amount">Bestanden <br>toegewezen: <span class="benodigd">0 / 1</span></span>
            </span>
         </td>
         <td class="plantmore-product-quantity" valign="top">
            <button class="uploadbutton btn yellowbtn dz-clickable">Bestand(en) uploaden</button>
         </td>
      </tr>
      <tr class="newrow">
         <td colspan="6">
            <form action="upload/uploaden.php" class="dropzone dropzoneform dz-clickable" maxfiles="1" id="dropzone4">
               <input type="hidden" value="Monomeer" name="productnaam">
               <input type="hidden" value="Twan" name="klantnaam">
               <input type="hidden" value="90" name="hoogte">
               <input type="hidden" value="90" name="breedte">
               <div class="dz-default dz-message"><span><i class="fas fa-file-upload uploadicon"></i> <span class="uploadtxt">Upload je bestand(en)</span></span></div>
            </form>
         </td>
      </tr>
   </tbody>
</table>

What can be causing the bugs? I tried removing the :last parts and only upload one image in the first one and one in the second. But the first preview still doesn’t have any information in it.

This is my custom previewelement inside .hiddendiv it’s beneath the dropzones in my DOM:

  <div class="hiddendiv">

    <div class="dz-preview dz-file-preview">
      <div class="dz-image"><img data-dz-thumbnail /></div>
      <div class="dz-details">
          <div class="dz-size"><span data-dz-size></span></div>
          <div class="dz-filename"><span data-dz-name></span></div>
      </div>
      <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
      <div class="dz-error-message"><span data-dz-errormessage></span></div>
            <span class="infoline">
                <span class="infospan bestandnaam">Bestandnaam:</span>
                <!-- <i class="fas fa-times-circle afgekeurd"></i> -->
            </span>
      <span class="infoline">
                <span class="infospan resolutie">Resolutie:</span>
                <!-- <i class="fas fa-check-circle goedgekeurd"></i> -->
            </span>
      <span class="infoline">
                <span class="infospan formaat">Formaat:</span>
                <!-- <i class="fas fa-times-circle afgekeurd"></i> -->
            </span>
            <div class="foutformaat">
                <span>Bestand heeft niet het benodigde formaat.</span>
                <span class="uploadinfobox">
                    <button class="infotooltip tooltipupload" data-tooltip="Lorem ipsum">
                        <i class="fas fa-info-circle"></i>
                    </button>
                </span>
            </div>
      <button class="yellowbtn btn vrijgevenbtn" type="button">Bestand vrijgeven</button>
            <hr class="uploadline">
            <span class="toewijzen">Aantal</span>
      <div class="uploadcontent">
        <input type="text" class="fileinput">
        <button class="plusminupload" id="minupload">−</button>
        <button class="plusminupload" id="plusupload">+</button>
      </div>
    </div>
  </div>

Updated code from answer below:

$('.dropzone').each(function(index, element){
    let $el = $(element);
    let $maxfiles = $el.attr('maxfiles');
    let $inputquantity = $el.find('input').val();
    let $thispreview = $el.find('.hiddendiv');
    let $thisdropzone = $el;
    // $maxfiles = $(this).attr('maxfiles');
    // $inputquantity = $(this).find('input').val();
    // $thisdropzone = $(this);
    // $thispreview = $(this).find('.hiddendiv');

    $(this).dropzone({
        // clickable: ".uploadbutton, .dropzoneform",
        paramName: 'postedFile',
        addRemoveLinks: true,
        dictDefaultMessage: '<i class="fas fa-file-upload uploadicon"></i> <span class="uploadtxt">Upload je bestand(en)</span>',
        dictRemoveFile: 'Verwijder',
        dictCancelUpload: 'Annuleren',
        dictInvalidFileType: 'Dit type bestand is niet toegestaan',
        dictCancelUploadConfirmation: 'Weet je zeker dat je het uploaden wilt annuleren?',
        dictMaxFilesExceeded: 'Maximale aantal bestanden overschreden',
        maxFiles: $maxfiles,
        acceptedFiles: '.jpg, .jpeg, .png, .pdf, .tif, .tiff',
        thumbnailWidth: '205',
        thumbnailHeight: '140',
        thumbnailMethod: 'crop',
        previewTemplate: $thispreview.html(),
        processing: function (file) {

        },
        // File contains dropzone file object, response contains ajax response from php file
        success: function (file, response) {
            // alert('test');
            let file_meta = JSON.parse(response);
            let $preview = $thisdropzone.find('.dz-preview:last');
            if(file_meta[0].status == 'success'){

            }else if(file_meta[0].status == 'error'){
                $preview.find('.vrijgevenbtn').show();
                $preview.find('.foutformaat').show();
            }
            $preview.find('.bestandnaam').text('Bestandsnaam: ' + file_meta[0].filename);
            $preview.find('.resolutie').text('Resolutie: ' + file_meta[0].dpi + ' DPI');
            $preview.find('.formaat').text('Formaat: ' + file_meta[0].heightcm + ' x ' + file_meta[0].widthcm + 'cm');
        },
    })
});

This is what happens when I upload multiple images in one selection:
Selecting three images from my computer:

enter image description here

Three previews in the dropzone with only the last one having data:

enter image description here

Every image returns a json string which in this example are:

[{"status":"error","filename":"instablok.jpg","filesize":22822,"tmp_name":"\/tmp\/phpI6ov6y","height":172,"width":565,"heightcm":"6,07","widthcm":"19,93","tifwidth":null,"dpi":"72"}]

[{"status":"error","filename":"fbblok.jpg","filesize":17349,"tmp_name":"\/tmp\/phpBzh40y","height":172,"width":565,"heightcm":"6,07","widthcm":"19,93","tifwidth":null,"dpi":"72"}]

[{"status":"error","filename":"logo-dark.png","filesize":21137,"tmp_name":"\/tmp\/phpCHGGZg","height":154,"width":881,"heightcm":"5,43","widthcm":"31,08","tifwidth":null,"dpi":"72"}]

All of them are returned seperately. The strange thing is, all data is added to the last preview and overwritten by a newer one. So the last preview first contains data from the first returned json, then the second and finally the last correct one, while the first two remain empty.

You can test with these two links:

First add a product to your basket here: https://printzelf.nl/new/folie/monomeer
You Aantal means quantity and this will determine how many images can be uploaded in a dropzone so you need more than 1 to be able to see the bug.

When you are done clicking through the options you can add it Toevoegen. Then go to the dropzone page link which is: https://printzelf.nl/new/bestanden-uploaden

How to&Answer:

scope your variables. You have this code.

$('.dropzone').each(function(index){
    $maxfiles = $(this).attr('maxfiles');
    $inputquantity = $(this).find('input').val();
    $thisdropzone = $(this);

So in reality the variables you are using are:

$maxfiles = window.maxfiles
$inputquantity = window.$inputquantity
$thisdropzone = window.$thisdropzone

This means that you are “reusing” the same variables on every dropzone field you’re iterating through, letting the last element looped through “stick” the variables. That’s why you are seeing your results always on the last dropzone on your page.

A slight visual representation
reusing the same variables

In code I recreated your behavior simplified. The theory is that all fields should show their hidden gems when the button is clicked. Yet only the last field shows the hidden gem. This is because the window.$selection variable is reassigned by each iteration.

$('.whoa').each(function(i,e) {
   $selection = $(e);
   $('.start').on('click', (e) => {
      $selection.val($selection.data("hidden-gem"));
   });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" class="whoa" data-hidden-gem="emerald">
<input type="text" class="whoa" data-hidden-gem="diamond">
<input type="text" class="whoa" data-hidden-gem="ruby">
<button class="start">Show gems</button>

What you want are scoped variables using var or better let keyword, making sure each looped element has their own set of variables relevant to their own loop.

scoped variables

In this code sample we see the desired behavior. The only difference the is the added let key word in front of $selection, forcing the creation of a scoped variable, resulting in the desired behaviour.

$('.whoa').each(function(i,e) {
   let $selection = $(e);
   $('.start').on('click', (e) => {
      $selection.val($selection.data("hidden-gem"));
   });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" class="whoa" data-hidden-gem="emerald">
<input type="text" class="whoa" data-hidden-gem="diamond">
<input type="text" class="whoa" data-hidden-gem="ruby">
<button class="start">Show gems</button>

Also try to refrain from using this in a jQuery context. It is not dependable. The meaning of this changes on every context change and can be altered altogether.

When looping through with $.each the first argument passed is the index, the second the actual element. This is a dependable scoped variable that will not change meaning.

Also, don’t instantiate multiple jQuery instances when you need to use the same thing several times. Instance once and re-use.

So with all this in mind rewrite those lines to:

$('.dropzone').each(function(index, element){
    let $el = $(element);
    let $maxfiles = $el.attr('maxfiles');
    let $inputquantity = $el.find('input').val();
    let $thisdropzone = $el;

Then you have these lines

$($thisdropzone).find('.dz-preview:last .bestandnaam').text('Bestandsnaam: ' + obj[0].filename);
$($thisdropzone).find('.dz-preview:last .resolutie').text('Resolutie: ' + obj[0].dpi + ' DPI');
$($thisdropzone).find('.dz-preview:last .formaat').text('Formaat: ' + obj[0].heightcm + ' x ' + obj[0].widthcm + 'cm');

You are re-wrapping something that is already a jQuery instance. Lose the unnecessary double wrapping.
Also consider putting obj[0] in a meaningfully names variable so the code becomes more readable, without having to check what we expect to contain in obj[0] in 3 months when we revisit the code.

Keeping this in mind, we can rewrite this to:

let file_meta = JSON.parse(response);
let $preview = $thisdropzone.find('.dz-preview:last');
$preview.find('.bestandnaam').text('Bestandsnaam: ' + file_meta.filename);
$preview.find('.resolutie').text('Resolutie: ' + file_meta.dpi + ' DPI');
$preview.find('.formaat').text('Formaat: ' + file_meta.heightcm + ' x ' + file_meta.widthcm + 'cm');

Also change let $preview = $thisdropzone.find('.dz-preview:last'); to let $preview = $(file.previewElement); so that when multiple files are uploaded, the correct preview element is used to render the relevant data.

A complete working example, minus upload capability due to no server accepting data.

let counter = 0;
$('.dropzone').each(function(index, element){
    let $el = $(element);
    let $maxfiles = $el.attr('maxfiles');
    let $inputquantity = $el.find('input').val();
    let uploaded_preview = $('.hiddendiv').html();
    let $thisdropzone = $el;
    // $maxfiles = $(this).attr('maxfiles');
    // $inputquantity = $(this).find('input').val();
    // $thisdropzone = $(this);
    // $thispreview = $(this).find('.hiddendiv');
    
    $el.dropzone({
        // clickable: ".uploadbutton, .dropzoneform",
        paramName: 'postedFile',
        addRemoveLinks: true,
        dictDefaultMessage: '<i class="fas fa-file-upload uploadicon"></i> <span class="uploadtxt">Upload je bestand(en)</span>',
        dictRemoveFile: 'Verwijder',
        dictCancelUpload: 'Annuleren',
        dictInvalidFileType: 'Dit type bestand is niet toegestaan',
        dictCancelUploadConfirmation: 'Weet je zeker dat je het uploaden wilt annuleren?',
        dictMaxFilesExceeded: 'Maximale aantal bestanden overschreden',
        maxFiles: $maxfiles,
        acceptedFiles: '.jpg, .jpeg, .png, .pdf, .tif, .tiff',
        thumbnailWidth: '205',
        thumbnailHeight: '140',
        thumbnailMethod: 'crop',
        previewTemplate: uploaded_preview,
        processing: function (file) {

        },
        // File contains dropzone file object, response contains ajax response from php file
        error: function (file, response) {
            
            counter++;
            console.log('Option ' + counter);
            response = '[{"status":"error","filename":"instablok'+counter+'.jpg","filesize":22822,"tmp_name":"\/tmp\/phpI6ov6y","height":172,"width":565,"heightcm":"6'+counter+',07","widthcm":"19'+counter+',93","tifwidth":null,"dpi":"72"}]';
            
            let file_meta = JSON.parse(response);
            
            let $preview = $(file.previewElement);
            if(file_meta[0].status == 'success'){

            }else if(file_meta[0].status == 'error'){
                $preview.find('.vrijgevenbtn').show();
                $preview.find('.foutformaat').show();
            }
            $preview.find('.bestandnaam').text('Bestandsnaam: ' + file_meta[0].filename);
            $preview.find('.resolutie').text('Resolutie: ' + file_meta[0].dpi + ' DPI');
            $preview.find('.formaat').text('Formaat: ' + file_meta[0].heightcm + ' x ' + file_meta[0].widthcm + 'cm');
        },
    })
});
.hiddendiv {
   display: none;
}

Answer:

And rename your hiddenDiv to hiddenDiv1 and hiddenDiv2, If needed you could also add the hiddenDivs dynamically and use the counterId for the name.