Element click event removed after adding new element with AJAX - javascript

I am trying to make a gallery app with dynamically loaded images with python flask framework and vanilla JS. Also, I made a full image viewer, so when you click on an image, it opens in a larger scale.
When I open the page for the first time, all works fine. But when load new images, the event, that triggers full viewer is removed on all images in gallery.
What's the reason?
Here is code.
Python. Sending list of images url:
#app.route('/loadmore', methods=['GET'])
def load_more():
return jsonify(get_images(5))
JS. XMLHttpRequest to get images url's:
function loadMore() {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
addImages(xhr.responseText);
}
};
xhr.open("GET", "/loadmore", true);
xhr.send();
}
JS. Adding new images to HTML:
function addImages(paths) {
let images = document.getElementById("images");
paths = JSON.parse(paths);
paths.forEach((path) => {
images.innerHTML += `<img class="grid-image" src="static/images/${path}">`;
});
}
Before loading new images
After loading new images

One of the solutions: add event listener every time, when load new images.
add new images to HTML
function addImages(paths) {
let images_grid = document.getElementById("images");
paths = JSON.parse(paths);
paths.forEach((path) => {
images_grid.innerHTML += `<img class="grid-image" src="static/images/${path}">`;
});
addClickEvent();
}
add event listener
function addClickEvent() {
const images = document.querySelectorAll(".grid-image");
images.forEach((el) => {
el.addEventListener("click", (e) => {
full_viewer.children[0].setAttribute("src", e.target.getAttribute("src"));
full_viewer.classList.toggle("hidden");
});
});
}

you can add event on every img tag .
function full_image_viewer(){}
function addImages(paths) {
let images = document.getElementById("images");
paths = JSON.parse(paths);
paths.forEach((path) => {
const img = document.createElement('img')
img.className = 'grid-image';
img.src = `static/images/${path}`;
img.addEventListener('click',full_image_viewer )
images.insertAdjacentElement('beforeend',img)
});
}

Related

How to know if a generated image is done?

I made a script in JS to find images on my page, launch a php script to remove background with imagick and save as png on my server.
How to know in JS when my png is ready ? I do this with a load event but I think I could do something better.
For now:
const checkImage = (path,imgs) =>
new Promise(resolve => {
const img = new Image();
img.onload = () => {
/* if(...){...} */
return resolve({path, status: 'ok'});
}
img.onerror = () => resolve({path, status: 'error'});
img.src = path;
});

Unable to preview image after converting attachment to Base64 using Trix

I'm using Trix, and for uploading attachments our backend developer tells me that I should convert the attachment to base64 and save the base64 data to the database instead of uploading the binary file.
I wrote this code for implementing it, and the output of the input field(HTML) is working as expected, but the image preview doesn't show in the editor.
$(function() {
$(document).on('trix-attachment-add', function(event) {
var attachment = event.originalEvent.attachment;
// Convert data URLs to File objects.
if(attachment.file) {
return uploadAttachment(attachment);
} else {
console.error('Could not upload attachment.');
console.error(attachment);
attachment.remove();
}
});
});
function uploadAttachment(attachment) {
var reader = new FileReader();
console.log(attachment)
// Set the reader to insert images when they are loaded.
reader.onload = function (e) {
var result = e.target.result;
var attrs = {
src : result,
url: result,
href : ''
};
attachment.setAttributes(attrs)
attachment.setUploadProgress(100);
}
// Read image as base64.
reader.readAsDataURL(attachment.file);
}
I don't know what causes this problem.
Try replacing
$(document).on('trix-attachment-add', function(event) {
with
document.addEventListener("trix-attachment-add", function(event) {
This could be event listeners being cached thus firing multiple times. The first load of image works, it could be the next loads that make this look busted.
Could also be Turbolinks issue so wrap your code with this instead:
$(document).on('turbolinks:load', function() {
I've managed to solve the issue by setting the fileObjectURL property as shown below
attachment.attachment.fileObjectURL = result;
Complete code for latest version (works with Symfony 6 easy admin bundle):
(function() {
function asBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
document.addEventListener("trix-file-accept", function(event) {
event.preventDefault();
if (event.file) {
asBase64(event.file).then(function(data) {
let image = document.createElement('img');
image.src = data;
let tmp = document.createElement('div');
tmp.appendChild(image);
let editor = document.querySelector('trix-editor');
editor.editor.insertHTML(tmp.innerHTML);
}).catch(e => console.log(e));
}
}); })();

Electron works unexpectedly with updating component's style

The code below works perfectly in browser, but not in Electron env.
function listenFileInput() {
fileInput.addEventListener('change', ev => {
startProgress();
const file = ev.target.files[0];
if (!file) return clearProgress();
loadImage(file);
});
}
function loadImage(file) {
const image = new Image();
image.onload = function() {
const src = cropImage(this);
cardImage.src = src;
clearProgress();
};
image.src = window.URL.createObjectURL(file);
}
function startProgress() {
fileBtn.setAttribute('disabled', true);
fileInput.setAttribute('disabled', true);
progress.style.display = 'flex';
}
function clearProgress() {
fileBtn.removeAttribute('disabled');
fileInput.removeAttribute('disabled');
progress.style.display = 'none';
}
In Electron env, when the file is loaded, the progress doesn't show up.
After do some tests, I found some interesting phenomenon:
If I comment the image.onload = function() {...} block, it works properly.
If I add alert() in onChange event callback or startProgress function, after alerting, the progress appears as expected.
If I comment clearProgress(); in image.onload callback, after the image was loaded, the progress appears.
So, it seems that the setAttribute and style.display didn't work (or Electron didn't re-render the page) until the image was loaded, unless there's an alert disturbs the process.
I've pushed the complete code to GitHub (/lib/file.js).

Pass image src to function instead of files

So I have a webpage with two image elements. It is basically a website where you upload an image and it encrypts a secret massage with steganography. I want to show the difference that is not otherwise visible and I found Resemble.js which is a library to compare images. It gets two files as arguments and I would like to use my image sources instead of files since I don't want to save the images generated.
To sum up, I want to get rid of the requests and get my images via sources in the HTML but I don't know how to get it to work with Resemble.js since it accepts only files.
How the second image is generated:
cover.src = steg.encode(textarea.value, img, {
"width": img.width,
"height": img.height
});
The JavaScript working with files:
(function () {
var xhr = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
var done = $.Deferred();
var dtwo = $.Deferred();
try {
xhr.open('GET', 'static/original.png', true);
xhr.responseType = 'blob';
xhr.onload = function (e) { done.resolve(this.response); };
xhr.send();
xhr2.open('GET', 'static/encoded.png', true);
xhr2.responseType = 'blob';
xhr2.onload = function (e) { dtwo.resolve(this.response); };
xhr2.send();
} catch (err) {
alert(err);
}
$('#example-images').click(function () {
$.when(done, dtwo).done(function (file, file1) {
if (typeof FileReader === 'undefined') {
resembleControl = resemble('./static/original.png')
.compareTo('./static/encoded.png')
.onComplete(onComplete);
} else {
resembleControl = resemble(file)
.compareTo(file1)
.onComplete(onComplete);
}
});
return false;
});
}());

how to call function after two different event complete

I need to initialize cropper plugin in modal pop up. Whenever user click on image uploader I want to show that image in popup and it should initialize cropper plugin when modal pop up finishes its show animation as well as after image loaded completely.
Currently what is happening sometime initCroping function get called before image loaded and sometime it calls properly.
I want to call initCroping function after image loaded and after changing $("#crop-img") src, Finally it should check if modal pop up loaded completely then it should fire iniCroping function.
both events are unpredictable sometime modal pop up comes first sometimes image loads. I want to check both the event complete and then initCroping should call.
Is there any easy way to call function after these two events complete.
$('#cropModel').on('shown.bs.modal', function() {
//initCroping();
});
$(".upload").change(function(e){
var preview = $('#crop-img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
//preview.src = reader.result;
$(preview).attr("src",reader.result);
initCroping();
}, false);
if (file) {
reader.readAsDataURL(file);
}
});
simpliest method:
var counter = 2;
function fireInitCroping() {
--counter === 0 && initCroping();
}
$('#cropModel').on('shown.bs.modal', function() {
fireInitCroping();
});
$(".upload").change(function(e){
var preview = $('#crop-img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
//preview.src = reader.result;
$(preview).attr("src",reader.result);
fireInitCroping();
}, false);
if (file) {
reader.readAsDataURL(file);
}
});
With Screw-FileReader and a few promises works too
var shownPopup = new Promise(resolve =>
$('#cropModel').one('shown.bs.modal', () => resolve())
)
var loadedImage = new Promise((resolve, reject) => {
$(".upload").change(e => {
// create a new Image obj from Blob that resolves/reject onload or onerror
e.target.files[0].image().then(img => {
img.id = "crop-img"
$("#crop-img").replaceWith(img)
resolve()
} err => {
console.error('not an image')
reject(err)
})
})
})
// When both event's has fired then fire initCroping
Promise.all([shownPopup, loadedImage]).then(initCroping)

Categories

Resources