IntersectionObserver and ajax loaded content - javascript

I am working on website at the moment, that has some native lazy loading of images using IntersectionObserver the images that I am trying to load are loaded in via ajax as a result I don't think they are "loaded" into DOM on page load, and therefore the images cannot be lazyloaded, by lazy load codes looks like this,
const io = new IntersectionObserver((entries, observer) => {
// console.log(entries)
entries.forEach(entry => {
console.log('😍');
if (entry.isIntersecting) {
console.log('intersecting');
const img = entry.target;
const src = img.getAttribute('data-lazy');
img.setAttribute('src', src);
img.classList.add('fade');
observer.disconnect();
}
});
Is it possible to lazy load images that are part of content that is loaded via ajax?

Yes, it is possible. After you fetch your data and update your DOM, you need to call your observer again.
const io = new IntersectionObserver((entries, observer) => {
// console.log(entries)
entries.forEach(entry => {
console.log('😍');
if (entry.isIntersecting) {
console.log('intersecting');
const img = entry.target;
const src = img.getAttribute('data-lazy');
img.setAttribute('src', src);
img.classList.add('fade');
observer.disconnect();
}
});
});
io.observe(document.querySelectorAll('ímg'));
const fethNewData = () => {
// any ajax call
window.fetch().then((html) => {
//update DOM
document.querySelector('body').innerHTML = html;
//call observe again
io.observe(document.querySelectorAll('ímg'));
})
};

Related

Element click event removed after adding new element with AJAX

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)
});
}

trying to load a video element with nodejs. Getting Error: Not implemented: HTMLMediaElement.prototype.load

Trying to render a mp4 video with jsdom and fabric
const object = new (cls || Media)(videoElement, options);
object.set("url", url);
object.set("__options", options);
videoElement.loop = true;
const onSeeked = () => {
videoElement.pause();
// middle frame loaded and we can play
// now we can return ready video
callback && callback(object);
resolve(object);
videoElement.removeEventListener("canplay", onSeeked);
};
const onLoad = () => {
// Wait for video to load
videoElement.addEventListener("canplay", onSeeked);
// Seek middle frame - for sake of paused preview
// we will get result after another canplay event
videoElement.currentTime = videoElement.duration * 0.5;
videoElement.width = videoElement.videoWidth;
videoElement.height = videoElement.videoHeight;
videoElement.removeEventListener("canplay", onLoad);
videoElement.load();
videoElement.play();
};
videoElement.addEventListener("canplay", onLoad);
videoElement.addEventListener('error', (e) => {
callback && callback(null, true);
reject(e);
});
source.addEventListener('error', (e) => {
callback && callback(null, true);
reject(e);
});
videoElement.load();
Since this is server side nodejs I am using jsdom to createDOM elements.
Once it reaches videoElement.load() It gives the following error. I looked up jsdom architecture and it seems they dont support video mp4 load and play
Getting Error:
Not implemented: HTMLMediaElement.prototype.load
UPDATED:
I was able to get video frames with ffmpeg library but now how can i generate the video on a canvas with node?
current implementation with ffmpeg

'load' event listener for detect image is done loaded is not working in react

I try to console some message when image is fully loaded using 'load' listener, but the code did not work, how to properly write a 'load' event listener in react ? Thankyou
useEffect(() => {
window.addEventListener('load', (event) => {
const imageTest = document.querySelector('img')
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded)
})
}, [])
This is not how react works. You are trying to use load event within the component when everything else is already loaded within from <div id="root"></div>.
React is Single Page App. And for the whole document load happens once only :)
However for individual elements we can set onload and fire that event in componentDidMount() or in useEffect() Hook
UPDATE: For image load check you do something like. You can do this or even use useRef()
useEffect(() => {
const imageTest = document.querySelector('img');
imageTest.onload = ()=>{
// Image is loaded and your further steps :)
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded);
}
}, []);
There is also one more easy way to do this:
Using onLoad synthetic event right on the image element itself. Which I think should also work fine:
const ImageLoadDemo ()=> {
const handleImageLoaded =()=> {
console.log("Image was successfully loaded");
}
const handleImageErrored =()=> {
console.log("Image was not loaded successfully");
}
return (
<div>
<img
src="https://picsum.photos/200/300"
onLoad={handleImageLoaded}
onError={handleImageErrored}
/>
</div>
);
}
The way to listen image is successfully loaded in react component is just put onLoad on your <img> tag, for example :
const MyCompoent = () => {
return <img src="yourImageLink.png" onLoad={()=>{console.log('The Image is successfully loaded')} } />
}
instead console a message you can pass a function as well

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 trigger event listener in script with custom event

I am attempting to trigger an event listener inside a script on my website with a custom event, however, nothing seems to make it happen. It's supposed to tell the canvas to render an audio waveform after the component is mounted.
In my App.js componentDidMount():
let waveform = document.getElementById("audio-spectrum");
// Loads script waveform.js
const script = document.createElement("script");
script.src = '../waveform.js';
script.async = true;
App.appendChild(script);
let newWaveform = new Event("render");
waveform.dispatchEvent(newWaveform);
In waveform.js:
let waveform = document.getElementById('audio-spectrum');
waveform.addEventListener("render", function(event) {
event.preventDefault();
console.log("Something should happen");
renderWaveform();
}, false);
I was wondering if having the same element trigger and catch the response is the source of my problems, but I'm doing the same thing with audio playback and it's working fine.
Thanks in advance.
UPDATE:
The problem was not with the event/event listener, but with appending the script while the event was firing. Here is my promise-based solution:
In my App.js:
renderWave = () => {
let waveform = document.getElementById('audio-spectrum');
let renderEvent = new Event('render');
waveform.dispatchEvent(renderEvent);
}
mountAudioScript = () => {
let mediaHandler = document.getElementById("media-handler");
const script = document.createElement("script");
mediaHandler.appendChild(script);
script.src = '../waveform.js';
return new Promise(res => {
script.onload = res
})
}
componentDidMount() {
this.mountAudioScript().then(() => {
if(this.props.mediaUrl) {
this.renderWave();
} else {
// promptUpload()
}
});
Change to let newWaveform = new CustomEvent("render"). See more here https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent

Categories

Resources