Skip to content

Instantly share code, notes, and snippets.

@Qubadi
Last active May 22, 2025 14:04
Show Gist options
  • Save Qubadi/5793d9bd3fbc996fb35cee7e50031f33 to your computer and use it in GitHub Desktop.
Save Qubadi/5793d9bd3fbc996fb35cee7e50031f33 to your computer and use it in GitHub Desktop.
Jetformbuilder customize the Choose File and drag-and-drop of images
UPDATED: 04.11.2024
Copy the following HTML code and create a HTML snippet using your snippet plugins.
Paste the code into the plugin and save it.
Don't forget to add MIME types (image formats), for example, PNG and JPEG, in the media field where it shows 'Allow MIME types.
______________________________________________________
<style>
/* Custom styles for the file upload field */
.jet-form-builder__field-wrap.jet-form-builder-file-upload {
background: #ecf6ff !important;
border: 3px dashed #c7d4e1 !important;
padding: 20px !important;
text-align: center;
position: relative;
margin-bottom: 20px;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 6px !important;
transition: border-color 0.3s ease, background-color 0.3s ease;
}
/* Styles for the image upload */
.jet-form-builder-file-upload__file img {
display: block;
width: 100%;
height: 100% !important;
padding: 0;
margin: 0;
-o-object-fit: cover;
object-fit: cover;
-o-object-position: center center;
object-position: center center;
border-radius: 6px !important;
box-shadow: 0 0 30px -8px rgba(0, 0, 0, 0.24) !important;
}
/* Styles for image content */
.jet-form-builder-file-upload__content {
min-height: auto !important;
}
/* Styles for the custom "Choose File" and "File Uploaded" buttons */
.addfile {
padding: 10px 20px;
background-color: #0037fd !important;
color: #ffffff !important;
border: none !important;
font-size: 16px;
font-weight: 600;
cursor: pointer;
border-radius: 6px;
margin-top: 10px; /* Space between button and label */
}
.addfile:hover {
background-color: #000000 !important;
color: #ffffff !important;
}
/* Styles for the label that shows file upload status */
.labeladdfile {
padding: 6px 12px;
background-color: transparent;
color: #000000 !important;
font-size: 12px;
font-weight: 400;
display: block;
margin-top: 10px;
border-radius: 6px;
}
/* Class added when files are uploaded */
.files-uploaded {
background-color: #09b872 !important;
padding: 6px 12px;
color: #ffffff !important;
border-radius: 6px;
}
/* Hide the default file input visually */
.jet-form-builder-file-upload__input {
display: none;
}
/* Drag and drop hover effect */
.jet-form-builder__field-wrap.jet-form-builder-file-upload.dragover {
border-color: #0037fd !important;
background: rgba(0, 55, 253, 0.1) !important;
}
/* New styles for drag and drop text and icon */
.drag-drop-text {
font-size: 30px;
font-weight: 700;
color: #333;
margin-bottom: 10px;
display: flex;
flex-direction: column;
align-items: center;
transition: opacity 0.3s ease, height 0.3s ease;
height: auto;
}
.drag-drop-text.hidden {
opacity: 0;
height: 0;
overflow: hidden;
}
.drag-drop-text .icon {
font-size: 24px;
margin-bottom: 10px;
}
.allowed-mime-types {
font-size: 14px;
color: #666;
margin-bottom: 10px;
transition: opacity 0.3s ease, height 0.3s ease;
height: auto;
}
.allowed-mime-types.hidden {
opacity: 0;
height: 0;
overflow: hidden;
}
.allowed-mime-types .formats {
font-weight: bold;
}
.or-text {
font-size: 14px;
color: #666;
margin-bottom: 10px;
transition: opacity 0.3s ease, height 0.3s ease;
height: auto;
}
.or-text.hidden {
opacity: 0;
height: 0;
overflow: hidden;
}
/* Responsive adjustments */
@media (max-width: 767px) {
.addfile, .labeladdfile {
padding: 8px 16px;
font-size: 12px;
}
}
@media (max-width: 1024px) {
.addfile, .labeladdfile {
padding: 8px 16px;
font-size: 12px;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const { addAction } = window.JetPlugins.hooks;
addAction('jet.fb.input.makeReactive', 'set-upload-labels', function(input) {
if (!input?.nodes?.length || !input.nodes[0].classList.contains('jet-form-builder-file-upload__input')) {
return;
}
const $ = jQuery;
const upload = $(input.nodes[0]);
const fields = $(input.nodes[0].closest('.jet-form-builder-file-upload__fields'));
// Extract allowed MIME types from the input element and format them
let mimeTypes = upload.attr('accept')?.split(',').map(type => type.split('/')[1].trim()) || [];
if (!fields.find('.addfile').length) {
fields.prepend(`
<div class="drag-drop-text">
<i class="fas fa-file-upload icon"></i>
Drag and drop your files here
</div>
<div class="allowed-mime-types">
Files supported: <span class="formats">${mimeTypes.join(', ')}</span>
</div>
<div class="or-text">OR</div>
`);
fields.append(`<input type="button" class="addfile" value="Choose File"/>`);
fields.append(`<div class="labeladdfile"></div>`);
}
const labelAdd = fields.find('.labeladdfile');
const buttonAdd = fields.find('.addfile');
const dragDropText = fields.find('.drag-drop-text');
const allowedMimeText = fields.find('.allowed-mime-types');
const orText = fields.find('.or-text');
upload.css('display', 'none');
buttonAdd.click(function() {
upload.trigger('click');
});
function updateFileLabel() {
const fileCount = upload[0].files.length;
if (fileCount > 0) {
buttonAdd.val('File Uploaded');
labelAdd.html(`Uploaded files (${fileCount})`);
labelAdd.addClass('files-uploaded');
buttonAdd.css({ opacity: 1, transform: 'scale(1)' });
labelAdd.css({ opacity: 1, transform: 'scale(1)' });
} else {
resetFileLabel();
}
}
function resetFileLabel() {
// Smoothly hide the button and label
buttonAdd.css({ opacity: 0, transform: 'scale(0.9)' });
labelAdd.css({ opacity: 0, transform: 'scale(0.9)' });
// After the transition, reset them
setTimeout(() => {
buttonAdd.val('Choose File');
labelAdd.html('Choose File');
labelAdd.removeClass('files-uploaded');
buttonAdd.css({ opacity: 1, transform: 'scale(1)' });
labelAdd.css({ opacity: 1, transform: 'scale(1)' });
$('.jet-form-builder-file-upload__file').remove(); // Remove any previews
}, 300);
}
const fieldWrap = fields.closest('.jet-form-builder__field-wrap.jet-form-builder-file-upload');
fieldWrap.on('dragover', function(event) {
event.preventDefault();
event.stopPropagation();
fieldWrap.addClass('dragover');
dragDropText.addClass('hidden');
allowedMimeText.addClass('hidden');
orText.addClass('hidden');
});
fieldWrap.on('dragleave', function(event) {
event.preventDefault();
event.stopPropagation();
fieldWrap.removeClass('dragover');
if (input.value.get().length === 0) {
dragDropText.removeClass('hidden');
allowedMimeText.removeClass('hidden');
orText.removeClass('hidden');
}
});
fieldWrap.on('drop', function(event) {
event.preventDefault();
event.stopPropagation();
fieldWrap.removeClass('dragover');
let files = event.originalEvent.dataTransfer.files;
{
const dt = new DataTransfer();
// Keep previously selected
for (let i = 0; i < upload[0].files.length; i++) {
dt.items.add(upload[0].files[i]);
}
// Add newly dropped
for (let i = 0; i < files.length; i++) {
dt.items.add(files[i]);
}
// Remove duplicates
for (let i = 0; i < dt.files.length; i++) {
for (let j = i + 1; j < dt.files.length; j++) {
if (
dt.files[i].name === dt.files[j].name &&
dt.files[i].lastModified === dt.files[j].lastModified &&
dt.files[i].size === dt.files[j].size &&
dt.files[i].type === dt.files[j].type
) {
dt.items.remove(j);
j--;
}
}
}
files = dt.files;
}
// Assign files, trigger change, rely on JetFormBuilder’s validation
upload[0].files = files;
const eventChange = new Event('change', { bubbles: true });
upload[0].dispatchEvent(eventChange);
// Give JetFormBuilder time to detect error (if any)
setTimeout(() => {
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message')
.text().trim();
if (errorMessage) {
alert(errorMessage);
window.location.reload();
resetFileInput();
}
}, 300); // Increased to 300ms to ensure validation is done
});
// We rely on JetFormBuilder’s own validation
function validateFiles(files) {
return true;
}
function showErrorMessage() {
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message')
.text().trim();
if (errorMessage) {
alert(errorMessage);
resetFileInput();
setTimeout(() => {
window.location.reload();
}, 700);
}
}
/*
* Merge chosen files, remove duplicates,
* then let JetFormBuilder do its validation
*/
upload.on('change', function() {
let chosenFiles = upload[0].files;
{
const dt = new DataTransfer();
for (let i = 0; i < upload[0].files.length; i++) {
dt.items.add(upload[0].files[i]);
}
// Remove duplicates (same logic as drag-and-drop)
for (let i = 0; i < dt.files.length; i++) {
for (let j = i + 1; j < dt.files.length; j++) {
if (
dt.files[i].name === dt.files[j].name &&
dt.files[i].lastModified === dt.files[j].lastModified &&
dt.files[i].size === dt.files[j].size &&
dt.files[i].type === dt.files[j].type
) {
dt.items.remove(j);
j--;
}
}
}
chosenFiles = dt.files;
upload[0].files = chosenFiles;
}
// Wait for JetFormBuilder to validate before checking for errors
setTimeout(() => {
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message')
.text().trim();
if (errorMessage) {
alert(errorMessage);
window.location.reload();
resetFileInput();
} else {
// If no error, update the file label
updateFileLabel();
}
}, 300);
});
// Keep the real-time UI changes
input.value.watch(function() {
const fileCount = this.current.length;
const fileText = fileCount > 0 ? `Uploaded files (${fileCount})` : '';
buttonAdd.val(fileCount > 0 ? 'File Uploaded' : 'Choose File');
labelAdd.html(fileText);
labelAdd.toggleClass('files-uploaded', fileCount > 0);
if (fileCount > 0) {
dragDropText.addClass('hidden');
allowedMimeText.addClass('hidden');
orText.addClass('hidden');
} else {
dragDropText.removeClass('hidden');
allowedMimeText.removeClass('hidden');
orText.removeClass('hidden');
}
});
$(document).on('click', '.jet-form-builder-file-upload__file-remove', function() {
setTimeout(() => {
if ($('.jet-form-builder-file-upload__file').length === 0) {
resetFileLabel();
} else {
buttonAdd.val('File Uploaded');
labelAdd.html(`Uploaded files (${$('.jet-form-builder-file-upload__file').length})`);
labelAdd.addClass('files-uploaded');
buttonAdd.css({ opacity: 1, transform: 'scale(1)' });
labelAdd.css({ opacity: 1, transform: 'scale(1)' });
}
}, 100);
});
const successMessageObserver = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
const successMessage = $(mutation.addedNodes).find('.jet-form-builder-message--success');
if (successMessage.length > 0) {
resetFileLabel();
}
}
}
});
const formContainer = $('.jet-form-builder')[0];
if (formContainer) {
successMessageObserver.observe(formContainer, { childList: true, subtree: true });
}
function resetFileInput() {
upload.val('');
resetFileLabel();
input.value.set([]);
}
});
});
</script>
@Qubadi
Copy link
Author

Qubadi commented Nov 21, 2024

@Qubadi thanks so much for this. I'm having a few little issues and I was hoping you could help me make it work. Please take a look at the screen recording: https://www.loom.com/share/b9e939fc4bf74ce0993f1a656dc63379?sid=7fc7b541-6ef0-4b9f-bbda-f9d3d9d9fc6e

I've updated the image limit in your code to 5. The first issue is with the duplicate images being dropped in. When you upload an image, it goes in, when you then drop-in the same image, it won't appear since it's a duplicate which is correct, but the image still counts towards the limits. So in green bar you can see the duplicate image has been added in.

Now when I reach the limit of 5 images, I don't get any popup message informing about the limits.

Instead, all of the previously uploaded images will now disappear and I'm not able to re-upload or re-drop any more images even if there are currently no images showing in the drop zone.

So I wonder if this has anything to do with the first issue, basically the images still being all counted in and for that reason not being able to upload any more.

I hope this makes any sense, but the video recording should be quite self-explanatory.

Hello

Please add the lasted updated code, and i have try the code again from my side, and it works with any issue,
image
See the image too please. If u still have issue, please contact me on Facebook group for Crocoblock community.

@Karbi-Prathul
Copy link

@Qubadi Thank you for sharing the code! Please add or implement a new functionality that enables image rearrangement via drag-and-drop, including on mobile devices. Currently, rearranging images is not possible on mobile, so this enhancement would be highly beneficial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment