Fix file input bug

User
document.addEventListener("DOMContentLoaded", function () {

const cards = document.querySelectorAll(".uploadtrigger");
const fileInputs = document.querySelectorAll(".inputFileHidden");

if (cards && fileInputs) {
cards.forEach(card => {
card.addEventListener("click", function () {
const target = this.getAttribute("data-target");
const fileInput = document.getElementById(target);
fileInput.click();
});
});
}

// when file selected, start upload with progress
document.querySelectorAll(".inputFileHidden").forEach(input => {
input.addEventListener("change", function (e) {
const file = this.files?.[0];
const securityID = document.querySelector('input[name="SecurityID"]').value;
if (!file) return;

const inputId = this.id;
let objectID = 0;
if(document.getElementById('ObjectIDModal')){
objectID = document.getElementById('ObjectIDModal').value;
}
const uiWrap = document.querySelector(`.upload-ui[data-for="${inputId}"]`);
const progress = uiWrap?.querySelector('.progress');
const bar = uiWrap?.querySelector('.progress-bar');
const preview = uiWrap?.querySelector('.upload-preview');
const label = document.querySelector(`.uploadtrigger[data-target="${inputId}"] .file-name`);
const endpoint = document.querySelector(`.uploadtrigger[data-target="${inputId}"]`)?.getAttribute('data-action');
const fieldName = document.querySelector(`.uploadtrigger[data-target="${inputId}"]`)?.getAttribute('data-field') || this.name;
const hiddenId = uiWrap?.querySelector('.upload-image-id');
const table = document.querySelector(`.uploadtrigger[data-target="${inputId}"]`)?.getAttribute('data-table');
if (!endpoint) {
console.warn('No data-endpoint on uploadtrigger for', inputId);
return;
}

// show chosen filename
if (label) label.textContent = file.name;

// show basic preview immediately
if (file.type.startsWith('image/')) {
if (preview) {
const reader = new FileReader();
reader.onload = ev => {
preview.src = ev.target.result;
preview.style.display = 'block';
};
reader.readAsDataURL(file);
}
}else{

}
// show UI
if (uiWrap) uiWrap.style.display = 'block';
if (bar) { bar.style.width = '0%'; bar.textContent = '0%'; }

// build request
const formData = new FormData();
formData.append(fieldName, file); // e.g. "Bilder" or "Image"
formData.append('field',fieldName);
if (securityID) formData.append('SecurityID', securityID);
if (table) formData.append('table', table);
if (objectID!=0) formData.append('objectID', objectID);
const xhr = new XMLHttpRequest();
xhr.open('POST', endpoint, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

// progress
xhr.upload.onprogress = function (ev) {
if (ev.lengthComputable && bar) {
const pct = Math.round((ev.loaded / ev.total) * 100);
bar.style.width = pct + '%';
bar.textContent = pct + '%';
}
};

xhr.onload = function () {
try {
const res = JSON.parse(xhr.responseText || '{}');
if (xhr.status >= 200 && xhr.status < 300 && res.ok) {
// use canonical URL from server (published asset)
if(file.type.startsWith('image/')){
if (res.url && preview) preview.src = res.url;
}else{
alert("dsdsa");
if (res.url && preview) preview.href = res.url;
preview.textContent = 'View';
preview.style.display = 'block';
preview.target = '_blank';
}
file.value = '';

// if (res.id && hiddenId) hiddenId.value = res.id; // store ImageID for later submit
} else {
alert(res.message || 'Upload failed');
}
} catch (e) {
alert('Upload failed');
}
};

xhr.onerror = function () {
alert('Network error during upload');
};

xhr.send(formData);
});
});

});
jQuery(document).ready(function($) {
$('.dashboardwrapper').on('click', '.delete-btn', function(e) {
e.preventDefault();

if (!confirm("Datei wirklich löschen?")) return;

var btn = jQuery(this);
var field = btn.data('field');
var fileslot = btn.closest('.file-slot');
var objectID = btn.data('objectid');
var action = btn.data('action');
var table = btn.data('table');
var securityID = jQuery('input[name="SecurityID"]').val();

jQuery.ajax({
url: action + '?objectId=' + objectID,
method: 'POST',
data: {
field: field,
objectId: objectID,
table: table,
SecurityID: securityID
},
success: function(res) {
if (res.ok) {
alert("Datei gelöscht: " + field);
// update UI: remove preview, disable button
fileslot.fadeOut();
} else {
alert(res.error || "Fehler beim Löschen");
}
},
error: function(xhr) {
alert("Serverfehler: " + xhr.status);
}
});
});

});
when again click file option after uploaded earlier then upload not start without refresh page

Assistant
You’re running into the classic “same file re-select doesn’t fire `change`” issue + a small bug: you’re doing `file.value=''` (where `file` is a `File` object, not the “). Fixes:

1) Clear the input **before** opening the picker, so choosing the same file triggers `change`.
2) Keep a reference to the input element and clear it **after** upload (success or failure).
3) Optional: reset the progress UI each time.

Here are the exact changes to make:

document.addEventListener("DOMContentLoaded", function () {

  const cards = document.querySelectorAll(".uploadtrigger");

  if (cards) {
    cards.forEach(card => {
      card.addEventListener("click", function () {
        const target = this.getAttribute("data-target");
        const fileInput = document.getElementById(target);
        if (!fileInput) return;

        // ✅ important: clear the input so picking the same file fires "change"
        fileInput.value = "";
        fileInput.click();
      });
    });
  }

  // when file selected, start upload with progress
  document.querySelectorAll(".inputFileHidden").forEach(input => {
    input.addEventListener("change", function () {
      const inputEl = this;                 // ✅ keep a ref to the input element
      const file    = inputEl.files?.[0];
      const securityID = document.querySelector('input[name="SecurityID"]')?.value || "";
      if (!file) return;

      const inputId   = inputEl.id;
      let objectID = 0;
      const objectIDEl = document.getElementById('ObjectIDModal');
      if (objectIDEl) objectID = objectIDEl.value;

      const uiWrap    = document.querySelector(`.upload-ui[data-for="${inputId}"]`);
      const progress  = uiWrap?.querySelector('.progress');
      const bar       = uiWrap?.querySelector('.progress-bar');
      const preview   = uiWrap?.querySelector('.upload-preview');
      const trigger   = document.querySelector(`.uploadtrigger[data-target="${inputId}"]`);
      const label     = trigger?.querySelector('.file-name');
      const endpoint  = trigger?.getAttribute('data-action');
      const fieldName = trigger?.getAttribute('data-field') || inputEl.name;
      const hiddenId  = uiWrap?.querySelector('.upload-image-id');
      const table     = trigger?.getAttribute('data-table');

      if (!endpoint) {
        console.warn('No data-action on uploadtrigger for', inputId);
        return;
      }

      // filename
      if (label) label.textContent = file.name;

      // basic preview for images (instant)
      if (file.type.startsWith('image/') && preview && 'src' in preview) {
        const reader = new FileReader();
        reader.onload = ev => {
          preview.src = ev.target.result;
          preview.style.display = 'block';
        };
        reader.readAsDataURL(file);
      }

      // show/reset UI
      if (uiWrap) uiWrap.style.display = 'block';
      if (bar) { bar.style.width = '0%'; bar.textContent = '0%'; }

      // build request
      const formData = new FormData();
      formData.append(fieldName, file);
      formData.append('field', fieldName);
      if (securityID) formData.append('SecurityID', securityID);
      if (table) formData.append('table', table);
      if (objectID) formData.append('objectID', objectID);

      const xhr = new XMLHttpRequest();
      xhr.open('POST', endpoint, true);
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

      // progress
      xhr.upload.onprogress = function (ev) {
        if (ev.lengthComputable && bar) {
          const pct = Math.round((ev.loaded / ev.total) * 100);
          bar.style.width = pct + '%';
          bar.textContent = pct + '%';
        }
      };

      xhr.onload = function () {
        // ✅ always clear input so user can re-select the same file again
        inputEl.value = "";

        try {
          const res = JSON.parse(xhr.responseText || '{}');
          if (xhr.status >= 200 && xhr.status < 300 && res.ok) {

            // Update preview using final canonical URL from server
            if (res.url && preview) {
              if (file.type.startsWith('image/') && 'src' in preview) {
                preview.src = res.url;
                preview.style.display = 'block';
              } else {
                // support non-image: treat preview as a link if it's an <a>
                if ('href' in preview) {
                  preview.href = res.url;
                  preview.textContent = preview.textContent?.trim() || 'View';
                  preview.target = '_blank';
                  preview.style.display = 'inline-block';
                }
              }
            }

            // if (res.id && hiddenId) hiddenId.value = res.id;

          } else {
            alert(res.message || 'Upload failed');
          }
        } catch (e) {
          alert('Upload failed');
        }
      };

      xhr.onerror = function () {
        // ✅ also clear on error so user can retry with same file
        inputEl.value = "";
        alert('Network error during upload');
      };

      xhr.send(formData);
    });
  });

});

// jQuery delete stays the same
jQuery(document).ready(function($) {
  $('.dashboardwrapper').on('click', '.delete-btn', function(e) {
    e.preventDefault();
    if (!confirm("Datei wirklich löschen?")) return;

    var btn = $(this);
    var field = btn.data('field');
    var fileslot = btn.closest('.file-slot');
    var objectID = btn.data('objectid');
    var action = btn.data('action');
    var table = btn.data('table');
    var securityID = $('input[name="SecurityID"]').val();

    $.ajax({
      url: action + '?objectId=' + objectID,
      method: 'POST',
      data: {
        field: field,
        objectId: objectID,
        table: table,
        SecurityID: securityID
      },
      success: function(res) {
        if (res.ok) {
          alert("Datei gelöscht: " + field);
          fileslot.fadeOut();
        } else {
          alert(res.error || "Fehler beim Löschen");
        }
      },
      error: function(xhr) {
        alert("Serverfehler: " + xhr.status);
      }
    });
  });
});

### Why this fixes it
– `fileInput.value = ""` before `.click()` ensures selecting the **same** file fires `change`.
– `inputEl.value = ""` after upload (success/error) resets the input for the next attempt.
– Previously you had `file.value=''` which does nothing (that `file` is a `File`, not the input). This was preventing re-uploads without a page refresh.

If you also want to reset the preview/progress on every new selection (so the UI is clean before upload starts), add this right after you compute `uiWrap/bar/preview`:

if (bar) { bar.style.width = '0%'; bar.textContent = '0%'; }
if (preview) {
  if (file.type.startsWith('image/') && 'src' in preview) {
    preview.removeAttribute('src');
  } else if ('href' in preview) {
    preview.removeAttribute('href');
    preview.textContent = '';
  }
  preview.style.display = 'none';
}
User
var fileslot = btn.closest('.file-slot'); fileslot.fadeOut(); instead of fadeout i wanr each image under fileslot div dislay none
Assistant
Got it 👍 — right now you’re fading out the **whole `.file-slot` container**, but you only want to hide the **children (each uploaded image/preview) inside it**, leaving the `.file-slot` wrapper intact.

Just replace this line:

fileslot.fadeOut();

with this:

fileslot.find('.upload-preview').hide();   // hide preview images/links
fileslot.find('.progress').hide();         // hide progress bar if needed
fileslot.find('.file-name').text('');      // clear file name label

or if you literally want to hide *everything inside* `.file-slot`:

fileslot.children().hide();

That way the parent `.file-slot` stays in the DOM (not removed), but all its inner elements (image, link, bar, labels) get hidden.

✅ Example updated snippet:

success: function(res) {
    if (res.ok) {
        alert("Datei gelöscht: " + field);

        // instead of removing the whole slot, clear its children
        fileslot.children().hide();  

    } else {
        alert(res.error || "Fehler beim Löschen");
    }
}

Do you want the slot’s UI to be **reset to initial state** (like “Choose file” text + hidden preview), or literally just hide everything inside?

Leave a Comment

Your email address will not be published. Required fields are marked *